android截屏功能实现方式汇总包括后台截屏

Posted 用户_147

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android截屏功能实现方式汇总包括后台截屏相关的知识,希望对你有一定的参考价值。

前言

对于android实现截屏功能,简单讲述一下可行的方法和之间的利弊



使用canvas

View v = getWindow().getDecorView();
Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas();
canvas.setBitmap(bitmap);
v.draw(canvas);

ImageView image = findViewById(R.id.image);
image.setImageBitmap(bitmap);

以上就是最简单的一种形式,通过画布将当前页面变成bitmap,然后直接加载到ImageView中就能看到效果。
优点:

  1. 实现简单
  2. 无权限要求

缺点:

  1. 只能截应用页面,状态栏不会被截进去,对于全屏截图的需求不适用
  2. 因为getWindow()是Activity中的方法,所以只能在Activity中调用(无法后台截屏)


MediaProjectionManager

MediaProjectionManager是一个录屏的API,我们可以通过获取其中一帧的方式来得到截图。此方法网上教程很多,大家可以自行搜索。
优点:
没啥优点,只是多了一种截图方案

缺点:

  1. 代码比较复杂
  2. 每次截图前会弹窗获取权限,询问是否允许录屏,这对用户体验来说是致命的
  3. 无法后台截屏,因为需要调用Activity中的onActivityResult()方法


使用SurfaceControl.screenshot

SurfaceControl.screenshot是系统的截屏API,不受Activity的限制,所以可以用来实现后台截图、全屏截图、指定区域截图等功能。
具体使用方式可参考我的这篇文章:SurfaceControl.screenshot()用法
优点:

  1. 使用无限制,可以在后台使用
  2. 功能强大,且调用方式简单
  3. 不用向用户申请权限,同时也无任何弹窗显示

缺点:

  1. 系统API,必须有系统签名和系统级别权限,普通app用不了此方法


service中创建一个透明的activity,并在activity中调用截图方法

此方法未试验过,不过service确实可以在后台创建一个activity出来,但是限制很多(可参考我的这篇文章:从后台启动activity无效的原因)。同时activity虽然是透明的,但是页面切换的动画效果可能会暴露app的行为,总之并不是一个有效的方法。



修改并编译源码中的screencap类

优点:
优点不大,不建议对系统类进行改造,如果实力允许,使用此方法可以免掉一些绕圈圈的调用方式和系统的限制

缺点:
难度大,需要有源码编译和源码修改的能力

android后台截屏方案选型与实现

问题场景

在通过辅助功能获取用户等级的时候,发现他们对用户等级采取了非常与众不同的方案,当然该方案有效地阻挡了我的直接获取。

android后台截屏方案选型与实现_解决方案

大家可以看到有 荣誉等级 但是无法获取到下面的20的view

解决方案

为了获取这个等级,直接获取肯定是不行了,只能选择曲线救国。

一个大胆的想法出现在脑海中,

截图,然后识别数字

从理论上讲这是一个可行的方案。

显而易见该方案有两个步骤

1、截图

2、识别

因为识别还没有找到完美的解决方案,就先写下截图的事情。

选型

补充个背景知识,辅助服务和被监控的应用是没有办法直接沟通的,需要有系统帮助。

候选方案有以下几个:

方案名

优点

缺点

备注

view.getDrawingCache()

系统提供方案,可以准确获取bitmap

需要先获取到对应的view

因为无法获取到目标应用的view对象,方案排除

adb

可以直接获取到所有场景下的view

需要adb权限,权限不好获取,并且一些rom上隔一段时间需要重新授权

不能保障adb权限的获取,方案排除

Accessibility CAPABILITY_CAN_TAKE_SCREENSHOT

系统自带能力

api30可用

因为api要求太高,方案排除

模拟系统截图方法

系统方法

不同机型实现的方案不一样,存在兼容性问题

因为兼容成本问题,方案排除

MediaProjectionManager

系统方案

需要用户授权

因为是自用,权限可控,系统方案稳定,入选

实现 MediaProjectionManager 方案

  1. 获取MediaProjectionManager服务实例
mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
  1. 通过MediaProjectionManager创建请求屏幕捕捉的隐式Intent,发送到目标Activity。这时会显示一个弹窗,“xxx将开始截取您屏幕上显示的所有内容”,申请用户同意。
 Intent intent = new Intent(mediaProjectionManager.createScreenCaptureIntent());
startActivityForResult(intent, REQUEST_CODE);
  1. 在发送方Activity的onActivityResult(int requestCode, int resultCode, Intent data)处理请求结果,若用户同意了请求,就可以通过返回的结果获取MediaProjection对象执行后面的流程进行屏幕画面捕捉
    @Override
protected void onActivityResult(int requestCode, final int resultCode, final Intent data)
super.onActivityResult(requestCode, resultCode, data);
if (REQUEST_CODE == requestCode)

//通过返回的结果获取MediaProjection对象执行后面的流程进行屏幕画面捕捉
MediaProjection mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
//创建用于接收投影的容器
mImageReader = ImageReader.newInstance(mWidthPixels, mHeightPixels, PixelFormat.RGBA_8888, 2);
//通过MediaProjection创建创建虚拟显示器对象,创建后物理屏幕画面会不断地投影到虚拟显示器VirtualDisplay上,输出到虚拟现实器创建时设定的输出Surface上。
VirtualDisplay mVirtualDisplay = mediaProjection.createVirtualDisplay("mediaprojection", mWidthPixels, mHeightPixels,
mDensityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mImageReader.getSurface(), null, null);
//从容器中获取image
Image image = mImageReader.acquireLatestImage();
//获取bitmap
if (image != null)
final Image.Plane[] planes = image.getPlanes();
if (planes.length > 0)
final ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * mWidthPixels;
Bitmap bitmap = Bitmap.createBitmap(mWidthPixels + rowPadding / pixelStride, mHeightPixels, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
image.close();





整个代码进行拆解

3.1. 获取用户授权后的MediaProjection对象,用来处理后续的屏幕捕获

3.2. 创建用于接收投影的容器

3.3. 通过MediaProjection对象创建虚拟显示器VirtualDisplay,并将内容不断投影到3.2中创建的容器中的Surface上

3.4. 从3.2的容器中获取image对象

3.5. 从3.4的image对象中获取bitmap

至此,在当前页面截图的功能实现。

等等,我们是想要在后台截图的!!!

升级为后台截图

后台截图有两个方案

  1. 通过service来实现,在前台实现一个悬浮框,然后在上面操作
  2. 通过全局工具类来实现

因为我这个是要给辅助服务使用的,所以方案1不是我想要的,我就实现了方案2

后台截图工具类

  1. 将image对象传给工具类
    public static void setmImageReader(Activity activity,ImageReader mImageReader) 
ShotScreenUtil.mImageReader = mImageReader;
  1. 创建截图方法
    public static Bitmap getSystemScreenBitmap(int dx,int dy,int w,int h) 

Image image = mImageReader.acquireLatestImage();
if (image != null)
final Image.Plane[] planes = image.getPlanes();
if (planes.length > 0)
final ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * mWidthPixels;
Bitmap bitmap = Bitmap.createBitmap(mWidthPixels + rowPadding / pixelStride, mHeightPixels, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
image.close();
return Bitmap.createBitmap(bitmap,dx,dy,w,h);


return null;
  1. 简单来说就是将前台截图方案中的截图方法给移到了工具类中,这样在任何地方都可以获取到截图了

总结

遇到问题有时候可以绕一下。如果有多个方案备选,就做下对比,贴合自己的业务选择一个认为最优的方案去尝试、优化。最终总可以找到一个自己想要的方案。

关注公众号: arigeweixin ,取得更多联系

android后台截屏方案选型与实现_解决方案_02

以上是关于android截屏功能实现方式汇总包括后台截屏的主要内容,如果未能解决你的问题,请参考以下文章

android后台截屏方案选型与实现

Android截屏截图方法汇总(ActivityViewScrollViewListViewRecycleViewWebView截屏截图)

Android自己定义截屏功能,相似QQ截屏

求教怎么在android下实现截取任一屏幕,也就是拷屏!!!

android实现截屏操作

android实现截屏操作