MediaProjection是什么?

按照惯例,附上Google官方文档链接: 官方文档

A token granting applications the ability to capture screen contents and/or record system audio. The exact capabilities granted depend on the type of MediaProjection.
A screen capture session can be started through MediaProjectionManager.createScreenCaptureIntent(). This grants the ability to capture screen contents, but not system audio

根据文档介绍。MediaProjection是手环用户获取屏幕内容或者记录系统的界面视频。你需要什么权限取决于MediaProjection的类型。通过MediaProjectionManager.createScreenCaptureIntent()去请求系统的屏幕信息权限,但是不会录制声音。

MediaProjection的重要方法

官方文档里提供了四种方法,但是最重要的就是下面这个方法

返回值 说明
VirtualDisplay createVirtualDisplay(String name, int width, int height, int dpi, int flags, Surface surface, VirtualDisplay.Callback callback, Handler handler)Creates a VirtualDisplay to capture the contents of the screen.
参数 说明
name String: The name of the virtual display, must be non-empty.This value must never be null.
- 这个值不能为空,用途还没有搞明白
width int: The width of the virtual display in pixels. Must be greater than 0.
- 用px表示的确切的值,必须大于0,我们截图的话传进去屏幕宽度就好
height int: The height of the virtual display in pixels. Must be greater than 0.
- 同上,传入屏幕高度
dpi int: The density of the virtual display in dpi. Must be greater than 0.
- 传入屏幕的dpi值
flags int: A combination of virtual display flags. See DisplayManager for the full list of flags.
- virtual displays的标识组合
surface Surface: The surface to which the content of the virtual display should be rendered, or null if there is none initially.
- 这个是特别重要的一个参数,是我们的屏幕绘制的内容,会放在这个参数中回调
callback VirtualDisplay.Callback: Callback to call when the virtual display’s state changes, or null if none.
- VirtualDisplay.Callback的实例对象作为参数,当展示的状态发生变化时回调
handler Handler: The Handler on which the callback should be invoked, or null if the callback should be invoked on the calling thread’s main Looper.
- callback回调时要做的操作放在Handler里

不明白?操作一下

说再多的概念也没有实际写一个Demo来的实在,那就写一个。
开始我们已经了解到 要通过MediaProjectionManager.createScreenCaptureIntent()方法获取一个intent来获取权限。

Intent intent = mMediaProjectionManager.createScreenCaptureIntent();        startActivityForResult(intent, RESULT_CODE);

其中mMediaProjectionManager是MediaProjectionManager的一个实例,RESULT_CODE是一个int的变量,我设置的是1。经过以上操作之后运行app就会有一个弹窗如图所示:

点击开始就可以获取获取屏幕内容的权限了。我们使用了startActivityForResult方法来启动这个intent,就是为了获取返回值。

protected void onActivityResult(int requestCode, int resultCode, Intent data) {        super.onActivityResult(requestCode, resultCode, data);        switch (requestCode) {            case RESULT_CODE:                if (resultCode == Activity.RESULT_OK) {                    mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data);                    mMediaProjection.createVirtualDisplay("shot_image", windowManager.getDefaultDisplay().getWidth(),                            windowManager.getDefaultDisplay().getHeight(),                            displayMetrics.densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, imageReader                                    .getSurface(), null, null);                }                break;        }    }

这里在onActivityResult方法里用MediaProjectionManager类的getMediaProjection方法传入回传的数据实例化了MediaProjection的对象。
然后重头戏来了,我们调用了createVirtualDisplay方法,上文中已经对这个方法进行了详细的说明,前面几个不再详细说明,我们看看几个关键参数。

1、DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR

这个参数官方的说明是:Virtual display flag: Allows content to be mirrored on private displays when no content is being shown.大概意思就是屏幕开始展示的时候就会把内容传出来。

2、imageReader.getSurface()

这个参数实际上是通过ImageReader的getSurface()方法来获取一个surface对象。然后把截屏内容传给这个imageReader的实例来处理。

聊聊ImageReader

怎么画风一转开始聊ImageReader呢?其实到上一步,MediaProjection的任务已经完成了。接下来要把数据解析成bitmap然后再使用就好,可以保存到本地或者加载到ImageView控件上,这里我们直接加载到Imageview的控件上。

要转化就一步一步来,这个surface是什么?

A Surface is generally created by or from a consumer of image buffers (such as a SurfaceTexture, MediaRecorder, or Allocation), and is handed to some kind of producer (such as OpenGL, MediaPlayer, or CameraDevice) to draw into.

官方文档里说他就是一个Image的缓冲区,交给一些可以绘制Image的工具去绘制后存在这个缓冲区里。surface我们了解这么多就够了。

接下来看看ImageReader

The ImageReader class allows direct application access to image data rendered into a Surface

这是官方的解释,我们结合代码来看看ImageReader:

private void startCapture() {        mImageName = System.currentTimeMillis() + ".png";        Image image = imageReader.acquireLatestImage();        if (image == null)            return;        int width = image.getWidth();        int height = image.getHeight();        final Image.Plane[] planes = image.getPlanes();        final ByteBuffer buffer = planes[0].getBuffer();        int pixelStride = planes[0].getPixelStride();        int rowStride = planes[0].getRowStride();        int rowPadding = rowStride - pixelStride * width;        Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);        bitmap.copyPixelsFromBuffer(buffer);        bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);        if (bitmap != null)            iv_screen.setImageBitmap(bitmap);        image.close();    }

经过上面几行代码之后我们就把拿到的数据转换成了bitmap,这里面主要说明的有几个方法。

1、Image image = imageReader.acquireLatestImage();

acquireLatestImage()
Acquire the latest Image from the ImageReader’s queue, dropping older images.

获得最新的Image从ImageReader的队列里,丢掉旧的images。

这里斜体字可以不看
之前我通过这个方法判断获取的 Image是否为空,判断完以后再去获取发现还是空,最后看了官方文档才知道会丢掉这张Image所以你判断完的时候就再也获取不到了,所以就要先获取再判断

回归正题,下面就看看另外一句关键的代码

2、后面的Image转换为bitmap的代码,我还不是太懂。求告知。

Final

通过以上的操作就可以实现屏幕截图了,那段转换代码我决定去求助一下别人。主要过程都在博文里说了,大家可以自己多看看,不会也可以问我。

更多相关文章

  1. Android(安卓)获取通话记录
  2. Android(安卓)读写文件整理
  3. Android获取基站坐标代码
  4. Android使用HttpURLConnection获取数据
  5. android获取imei和imsi
  6. android解决UI阻塞问题——创建AsyncTask 子线程
  7. 浅谈Java中Collections.sort对List排序的两种方法
  8. Python list sort方法的具体使用
  9. python list.sort()根据多个关键字排序的方法实现

随机推荐

  1. 【DB宝43】MySQL误操作闪回恢复利器之my2
  2. 大数据成神之路-Java高级特性增强(Concur
  3. 数据搬运组件:基于Sqoop管理数据导入和导
  4. CentOS下搭建MySql(RPM包)
  5. 大数据成神之路-Java高级特性增强(多线程
  6. 大数据成神之路-Java高级特性增强(Volati
  7. DolphinDB客户端软件教程
  8. Think PHP框架清除运行时缓存(php文件目录
  9. 无法访问此卷不包含可识别的文件系统 chk
  10. FlinkSQL演进过程,解析原理及一些优化策略