关于Android 5.0以上截屏API MediaProjection的使用方式总结

1.MediaProjection 简介

对于Android 5.0以上,系统提供了一个可用于截(录)屏的API,这里主要讲一下利用MediaProjection截屏并取出图片byte[]数据的方法以及自己在使用过程中踩到的坑.
主要涉及到的类有MediaProjectionManager,MediaProjection,ImageReader,
VirtualDisplay等;

2.使用步骤

a.MediaProjection使用时需要经过用户授权因此需要一个Activity作为载体来完成授权操作,这里在参考网上的做法实现一个完全透明的Activity来完成授权操作:
startActivityForResult(((MediaProjectionManager)getSystemService(Context.MEDIA_PROJECTION_SERVICE)).createScreenCaptureIntent(),
                    REQUEST_MEDIA_PROJECTION
            );
a.获取MediaProjectionManager对象,该对象是一个系统服务,可通getSystemService(Context.MEDIA_PROJECTION_SERVICE)获取,
获取完成后利用manager创建授权需要的Intent:
Intent intent = mediaProjectionManager.createScreenCaptureIntent();
然后startActivityForResult(intent,requestCode);
然后在onActivityResult()方法中获取到授权成功的回执即Intent数据,需要注意的是MediaProjection的创建需要该Intent,如果不希望在onActivityResult中直接创建MediaProjection对象,那么需要将该Intent保存起来。

b.创建MediaProjection对象:
mMediaProjection = mediaProjectionManager.getMediaProjection(Activity.RESULT_OK, mRequestPermissionData);
c.创建读取数据需要的ImageReader
  mImageReader = ImageReader.newInstance(mScreenWidth, mScreenHeight, PixelFormat.RGBA_8888, 3);
d.创建虚拟的屏幕投影
 mVirtualDisplay = mMediaProjection.createVirtualDisplay("screen-mirror", mScreenWidth,
                mScreenHeight, mDensityDpi,
                DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                mImageReader.getSurface(), null, null);
e.读取截屏数据,自己在写代码的时候发现,以上对象的创建完成需要一定的时间,从创建对象到图片数据真正可用需要在OnImageAvailableListener的回调方法中完成:
    mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                if (mFirstScreencap) {
                    Image image = mImageReader.acquireNextImage();
                    if (image != null) {
                        updateImageParams(image.getWidth(), image.getHeight());
                        mFrameBytes = getRgbaDataFromImage(image);
                        if (onShotListener != null) {
                            onShotListener.onReady();
                        }
                        mAvailabled = true;
                        mFirstScreencap = false;
                        mImageReader.setOnImageAvailableListener(null, null);
                        image.close();
                    }
                }
            }
        }, mImageReaderHandler);
f.取出byte[]数据并完成图片数据修正
private static byte[] getRgbaDataFromImage(Image image) {
        int width = image.getWidth();
        int height = image.getHeight();
        final Image.Plane[] planes = image.getPlanes();
        final ByteBuffer buffer = planes[0].getBuffer();
        int rowStride = planes[0].getRowStride();
        byte[] data = new byte[width * height * BYTES_PER_PIXEL];
        if (rowStride == width * BYTES_PER_PIXEL) {
            buffer.get(data);
            return data;
        }
        ByteBuffer destBuffer = ByteBuffer.wrap(data);
        byte[] buf = new byte[rowStride];
        int byteCount = width * BYTES_PER_PIXEL;
        for (int i = 0; i < height; i++) {
            buffer.get(buf);
            destBuffer.put(buf, 0, byteCount);
        }
        return data;
    }
g.返回最终的byte[]数据
 public byte[] getFrameBuffer() {
        Image image = mImageReader.acquireNextImage();
        if (image == null) {
            return mFrameBytes;
        } else {
            mFrameBytes = getRgbaDataFromImage(image);
            updateImageParams(image.getWidth(), image.getHeight());
            image.close();
            return mFrameBytes;
        }
    }

3.总结

 mImageReader.acquireNextImage();注意该方法不是每时每刻都有值返回,只有当surface里面有新的缓存图像可用时才有数据返回,因此在使用时应该将每次取到的数据缓存起来,当没有新的图像返回时,直接使用上一次的缓存数据。
 ByteBuffer里面取出的byte数组中可能包含一些无用的数据信息,需要将其修正。

第一次写博客,希望大家别喷哈~~

你可能感兴趣的:(关于Android 5.0以上截屏API MediaProjection的使用方式总结)