设备:pixel2xl Android Q beat4
在给应用做适配测试的时候,发现MediaProjection报错了,如下:
java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
提升的信息很少,可能是关于安全的问题,MediaProjection必须在前台服务中进行。
servicesInfo是啥怎也不知道啊,确实很惭愧.......
网上找了下很少看见介绍ServicesInfo的,可以参考一下:
- http://amappc.cn-hangzhou.oss-pub.aliyun-inc.com/lbs/static/unzip/Android_Location_Doc/com/amap/api/location/AMapLocationClient.html
- https://developer.android.com/reference/android/content/pm/ServiceInfo
google的官网中显示确实在API29中增加了很多稀奇古怪的玩意。
好吧,我们来尝试解决下。
尝试一:
第一想法就是在manifest.xml中对services配置啥,我们加上以下信息:
<service
android:name=".ScreenRecorder"
android:foregroundServiceType="mediaProjection">
</service>
然后:
- sync
- run
- 嘴角疯狂上扬嘻嘻嘻
- emmmm......error
- 垃圾Android毁我青春,耗我钱财
尝试二:
对services配置:
<service
android:name=".ScreenRecorder"
android:enabled="true"
android:foregroundServiceType="mediaProjection"/>
MediaProjection获取屏幕数据主要是三步
1.//准备MediaProjection
mMediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
Intent captureIntent = mMediaProjectionManager.createScreenCaptureIntent();
startActivityForResult(captureIntent, 1);
2.在回调中将数据用intent传递到services中
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.e(TAG, "onActivityResult: " + resultCode);
if (resultCode == Activity.RESULT_CANCELED) {
Log.e(TAG, "User cancel");
} else {
try {
WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getMetrics(metrics);
} catch (Exception e){
Log.e(TAG, "MediaProjection error");
}
Intent service = new Intent(this, ScreenRecorder.class);
service.putExtra("code", resultCode);
service.putExtra("data", data);
startForegroundService(service);
}
}
3.mediaprojection必须在services中进行。
@Override
public int onStartCommand(final Intent intent, int flags, int startId) {
createNotificationChannel();
mResultCode = intent.getIntExtra("code", -1);
mResultData = intent.getParcelableExtra("data");
//mResultData = intent.getSelector();
mMediaProjection = mMediaProjectionManager.getMediaProjection(mResultCode, Objects.requireNonNull(mResultData));
//mMediaProjection = ((MediaProjectionManager) Objects.requireNonNull(getSystemService(Context.MEDIA_PROJECTION_SERVICE))).getMediaProjection(mResultCode, mResultData);
Log.e(TAG, "mMediaProjection created: " + mMediaProjection);
return super.onStartCommand(intent, flags, startId);
}
最后就可以通过mMediaProjection.createVirtualDisplay获取屏幕数据啦。
private void createNotificationChannel() {
Notification.Builder builder = new Notification.Builder(this.getApplicationContext()); //获取一个Notification构造器
Intent nfIntent = new Intent(this, MainActivity.class); //点击后跳转的界面,可以设置跳转数据
builder.setContentIntent(PendingIntent.getActivity(this, 0, nfIntent, 0)) // 设置PendingIntent
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher)) // 设置下拉列表中的图标(大图标)
//.setContentTitle("SMI InstantView") // 设置下拉列表里的标题
.setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标
.setContentText("is running......") // 设置上下文内容
.setWhen(System.currentTimeMillis()); // 设置该通知发生的时间
/*以下是对Android 8.0的适配*/
//普通notification适配
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId("notification_id");
}
//前台服务notification适配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel("notification_id", "notification_name", NotificationManager.IMPORTANCE_LOW);
notificationManager.createNotificationChannel(channel);
}
Notification notification = builder.build(); // 获取构建好的Notification
notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
startForeground(110, notification);
}