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 ,取得更多联系

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android中,截屏需要访问系统级别的权限,因此在后台Service中进行截屏需要获取特殊的权限。以下是一些实现步骤: 1. 在AndroidManifest.xml文件中添加权限声明: ``` <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> ``` 2. 创建一个Service,并在onCreate()方法中获取WindowManager和DisplayMetrics对象: ``` private WindowManager mWindowManager; private DisplayMetrics mDisplayMetrics; @Override public void onCreate() { super.onCreate(); mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); mDisplayMetrics = new DisplayMetrics(); mWindowManager.getDefaultDisplay().getMetrics(mDisplayMetrics); } ``` 3. 在Service中创建一个Bitmap对象,并使用MediaProjectionManager类来获取MediaProjection对象: ``` private MediaProjectionManager mMediaProjectionManager; private MediaProjection mMediaProjection; private ImageReader mImageReader; private int mScreenWidth; private int mScreenHeight; @Override public int onStartCommand(Intent intent, int flags, int startId) { mMediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE); mMediaProjection = mMediaProjectionManager.getMediaProjection(Activity.RESULT_OK, (Intent) intent.getParcelableExtra("data")); mScreenWidth = mDisplayMetrics.widthPixels; mScreenHeight = mDisplayMetrics.heightPixels; mImageReader = ImageReader.newInstance(mScreenWidth, mScreenHeight, PixelFormat.RGBA_8888, 1); mMediaProjection.createVirtualDisplay("ScreenCapture", mScreenWidth, mScreenHeight, mDisplayMetrics.densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mImageReader.getSurface(), null, null); mHandler.postDelayed(new Runnable() { @Override public void run() { startCapture(); } }, 1000); return super.onStartCommand(intent, flags, startId); } private void startCapture() { Image image = mImageReader.acquireLatestImage(); if (image != null) { Bitmap bitmap = Bitmap.createBitmap(mScreenWidth, mScreenHeight, Bitmap.Config.ARGB_8888); bitmap.copyPixelsFromBuffer(image.getPlanes()[0].getBuffer()); image.close(); // 在这里进行截屏操作 } } ``` 4. 在startCapture()方法中进行截屏操作,并将截屏结果保存到文件或者发送到服务器等。 需要注意的是,上述代码只是截取屏幕的一帧,如果需要实现视频录制或者连续截屏,需要做相应的修改。此外,由于截屏需要访问系统级别的权限,因此需要确保用户已经同意了相关权限。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值