Android 12下MediaProjectionManager.getMediaProjection()二次调用报错问题
我开发的截图应用在多数Android设备上运行正常,但在Android 12的小米11T设备中出现问题:使用静态保存的resultCode和Intent,克隆后传入mediaProjectionManager.getMediaProjection(resultCode, clonedIntent)方法,首次截图正常,第二次截图时触发如下空指针异常:
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.koala.classor, PID: 10824 java.lang.NullPointerException: Attempt to invoke interface method 'boolean java.util.Set.add(java.lang.Object)' on a null object reference at android.os.Parcel.createExceptionOrNull(Parcel.java:2431) at android.os.Parcel.createException(Parcel.java:2409) at android.os.Parcel.readException(Parcel.java:2392) at android.os.Parcel.readException(Parcel.java:2334) at android.media.projection.IMediaProjection$Stub$Proxy.start(IMediaProjection.java:235) at android.media.projection.MediaProjection.<init>(MediaProjection.java:59) at android.media.projection.MediaProjectionManager.getMediaProjection(MediaProjectionManager.java:119) at com.koala.classor.G.getMediaProjection(G.java:86) at com.koala.classor.OverScreenWindowService$2$1.run(OverScreenWindowService.java:194) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:210) at android.os.Looper.loop(Looper.java:299) at android.app.ActivityThread.main(ActivityThread.java:8105) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1045) Caused by: android.os.RemoteException: Remote stack trace: at com.android.server.media.projection.MediaProjectionManagerService$MediaProjection.start(MediaProjectionManagerService.java:553) at android.media.projection.IMediaProjection$Stub.onTransact(IMediaProjection.java:137) at android.os.Binder.execTransactInternal(Binder.java:1182) at android.os.Binder.execTransact(Binder.java:1146)
原因分析
这个异常的核心是MediaProjection的权限Intent只能被使用一次。Android 12及小米MIUI这类定制系统对MediaProjection的权限生命周期管控更严格:第一次调用getMediaProjection后,系统会标记该Intent对应的权限已被消耗,第二次复用(哪怕是克隆)同一个Intent时,系统内部的权限状态已失效,导致服务端抛出空指针。
另外,静态保存resultCode和Intent的方式本身存在风险,应用进程重启或权限状态变更后,这些值会直接失效。
解决步骤
每次截图前重新申请权限,不复用旧Intent
放弃静态保存权限参数的方式,每次需要截图时重新启动MediaProjection权限请求流程:private static final int REQUEST_CODE_SCREEN_CAPTURE = 1001; private void requestScreenCapturePermission() { MediaProjectionManager manager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE); Intent intent = manager.createScreenCaptureIntent(); startActivityForResult(intent, REQUEST_CODE_SCREEN_CAPTURE); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_SCREEN_CAPTURE && resultCode == RESULT_OK) { // 仅在本次截图流程中使用该data,不做静态保存 MediaProjection projection = mediaProjectionManager.getMediaProjection(resultCode, data); performScreenshot(projection); } }及时释放MediaProjection资源
截图完成后立即调用projection.stop()释放资源,避免系统中残留无效实例:private void performScreenshot(MediaProjection projection) { // 初始化VirtualDisplay、ImageReader等截图组件 // ... 执行截图逻辑 ... // 截图完成后立即释放 projection.stop(); }不要克隆权限Intent
克隆操作可能丢失系统内部绑定的权限状态信息,直接使用onActivityResult返回的原始data即可,无需额外处理。适配Android 12后台权限限制
Android 12禁止后台应用直接申请MediaProjection权限,确保权限申请操作在应用前台页面触发,不要在后台服务中直接调用。
额外注意事项
- 若需持续截图(如录屏),可保留同一个MediaProjection实例直到停止操作后再释放,但单次截图必须每次重新申请权限。
- 小米MIUI系统需确保应用已获得「悬浮窗权限」和「屏幕录制权限」,部分机型需用户手动在设置中开启。
内容的提问来源于stack exchange,提问作者reza r h

