You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用MediaProjection.createVirtualDisplay出现已启动和空指针异常如何解决

错误根因

  • 第一个IllegalStateException: Cannot start already started MediaProjection错误:循环中频繁调用startCapture(),上一次创建的MediaProjection实例还未释放就重复初始化。
  • 第二个NullPointerException错误:四类场景触发:1. 启动服务时未正确传入截图授权的resultCode和resultData,导致getMediaProjection返回空;2. stopCapture方法未对vdisplay做非空判断,释放时报错;3. 多线程并发调用startCapture和stopCapture,状态不同步导致空引用;4. Android 10及以上未启用前台服务,系统自动回收MediaProjection实例。

修复方案

1. 改造AbstractNotificationService类

添加状态标记、并发锁、空值校验:

public abstract class AbstractNotificationService extends AccessibilityService {
    // 新增截图状态标记
    private boolean isCapturing = false;

    // 原有代码不变...

    private void stopCapture() {
        // 加锁保证线程安全
        synchronized (this) {
            if (projection!=null) {
                projection.stop();
                // 补充vdisplay非空判断
                if (vdisplay != null) {
                    vdisplay.release();
                    vdisplay = null;
                }
                projection=null;
            }
            isCapturing = false;
        }
    }

    public void startCapture() {
        synchronized (this) {
            // 先清理未释放的实例,避免重复启动
            if (isCapturing || projection != null) {
                stopCapture();
            }
            // 校验授权参数合法性,避免空指针
            if (resultData == null || resultCode == 1337) {
                Log.e("CaptureError", "截图授权参数未初始化,无法启动");
                return;
            }
            isCapturing = true;
            projection=mgr.getMediaProjection(resultCode, resultData);
            // 校验MediaProjection创建结果
            if (projection == null) {
                Log.e("CaptureError", "MediaProjection创建失败,检查权限有效性");
                isCapturing = false;
                return;
            }
            it=new ImageTransmogrifier(this);
            if (it.getSurface() == null) {
                Log.e("CaptureError", "Surface实例为空,无法创建虚拟显示器");
                stopCapture();
                return;
            }

            MediaProjection.Callback cb=new MediaProjection.Callback() {
                @Override
                public void onStop() {
                    if (vdisplay != null) {
                        vdisplay.release();
                        vdisplay = null;
                    }
                    isCapturing = false;
                }
            };

            vdisplay=projection.createVirtualDisplay("andshooter",
                    it.getWidth(), it.getHeight(),
                    getResources().getDisplayMetrics().densityDpi,
                    VIRTUAL_DISPLAY_FLAGS, it.getSurface(), null, handler);
            projection.registerCallback(cb, handler);
        }
    }

    // 对外暴露状态判断方法
    public boolean isCapturing() {
        return isCapturing;
    }
}

2. 修正MainActivity服务启动时机

确保拿到截图授权后再启动服务,避免传参为空:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_SCREENSHOT) {
        if (resultCode == RESULT_OK) {
            resultCodeX=resultCode;
            dataX=data;
            // 拿到截图权限后再请求悬浮窗权限
            askForSystemOverlayPermission();
        } else {
            Toast.makeText(this, "截图权限被拒绝,无法使用功能", Toast.LENGTH_SHORT).show();
            finish();
        }
    } else if (requestCode == DRAW_OVER_OTHER_APP_PERMISSION) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (Settings.canDrawOverlays(this)) {
                // 两个权限都拿到后再启动服务
                startService();
            } else {
                errorToast();
                finish();
            }
        }
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

3. 调整自定义线程调用逻辑

避免无间隔频繁发起截图请求:

@Override
public void run() {
    while(循环条件){
        // 仅当前无正在执行的截图任务时才发起新请求
        if (!notificationService.isCapturing()) {
            notificationService.startCapture();
        }
        // 按需添加截图间隔,避免占用过多系统资源
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            break;
        }
    }
    notificationService.sendNotification("ENDED");
}

4. 补充前台服务逻辑

Android 10(API 29)及以上版本使用MediaProjection必须绑定前台服务,你需要放开onStartCommand中注释的foregroundify()逻辑,添加前台服务通知,否则系统会自动回收MediaProjection实例导致空指针。

内容的提问来源于stack exchange,提问作者zaxunobi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 13:24:08