You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android API<25使用MediaProjection截图空指针异常问题求助

Alright, let's fix this issue for you. First, that NullPointerException is happening because your screenshotPermission Intent is null—you can't call clone() on a null object, which makes total sense. Here's what's wrong and how to fix it properly for API <25, plus a solution for the video black screen problem:

1. Root Cause of the NPE

To use MediaProjection, you must first request user permission via the system's consent dialog—you can't skip this step and try to use an uninitialized Intent. The system only returns a valid permission Intent after the user grants access, so your current code is trying to clone a null value, hence the crash.

2. Step-by-Step Fix for API <25 Screenshots

First: Trigger the MediaProjection Permission Request

Add this code where you want to start the screenshot flow (e.g., a button click):

private static final int REQUEST_MEDIA_PROJECTION = 1001;
private Intent screenshotPermission; // Will hold the permission Intent after user consent

// Call this method to start the permission flow
private void requestScreenCapturePermission() {
    MediaProjectionManager mediaProjectionManager = 
        (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
    startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);
}

Second: Handle the Permission Result

Override onActivityResult to capture the valid permission Intent once the user grants access:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_MEDIA_PROJECTION && resultCode == RESULT_OK) {
        screenshotPermission = data; // Save the permission Intent here
        // Now proceed with the actual screenshot
        captureScreenWithMediaProjection();
    }
}

Third: Implement the Screenshot Logic (With Async Handling)

The ImageReader captures frames asynchronously—you can't call acquireLatestImage() immediately after creating the virtual display. Use a listener to get notified when a frame is ready:

private MediaProjection mediaProjection;
private ImageReader mImageReader;
private Bitmap image;
private Handler mHandler = new Handler(Looper.getMainLooper());

private void captureScreenWithMediaProjection() {
    if (screenshotPermission == null) return;

    final DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);
    final int width = metrics.widthPixels;
    final int height = metrics.heightPixels;
    final int densityDpi = metrics.densityDpi;
    final int MAX_IMAGES = 10;

    MediaProjectionManager mediaProjectionManager = 
        (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
    try {
        // Now clone the valid permission Intent
        mediaProjection = mediaProjectionManager.getMediaProjection(RESULT_OK, (Intent) screenshotPermission.clone());
        
        mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, MAX_IMAGES);
        // Set listener to handle captured frames
        mImageReader.setOnImageAvailableListener(reader -> {
            // Use try-with-resources to auto-close the Image
            try (Image img = reader.acquireLatestImage()) {
                if (img != null) {
                    byte[] datamm = Utils.getDataFromImage(img);
                    image = BitmapFactory.decodeByteArray(datamm, 0, datamm.length);
                    // Resize the bitmap after successful capture
                    image = Utils.getResizedBitmap(image, 500);
                    // Do your work with the screenshot bitmap here (e.g., save, send)
                    
                    // Clean up resources to avoid leaks
                    mediaProjection.stop();
                    mImageReader.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, mHandler);

        // Create virtual display with flags to fix video black screen
        mediaProjection.createVirtualDisplay("ScreenCapture", 
            width, height, densityDpi,
            DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC | DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
            mImageReader.getSurface(), null, null);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

3. Fixing the Video Black Screen Issue

The black screen when capturing video is usually caused by incorrect virtual display flags. Use VIRTUAL_DISPLAY_FLAG_PUBLIC alongside AUTO_MIRROR—this ensures the virtual display can capture content from surface views (like video players) that use secure rendering surfaces. Also, waiting for the onImageAvailable callback ensures you only capture the frame once it's fully rendered.

4. Additional Best Practices

  • Always clean up resources: Call mediaProjection.stop() and mImageReader.close() after you're done with the screenshot to prevent memory leaks.
  • For API 26+, your existing PixelCopy code should work, but if you still get black screens for video, confirm you're capturing the correct window (some video players render outside the activity window, so you might need to adjust your PixelCopy target).

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

火山引擎 最新活动