关于Android5.0以上屏幕截图探索总结

前言

做过Android屏幕截图的朋友应该知道在Android5.0之前如果希望截图屏幕,是需要获取系统root权限的。但,在5.0之后Android开放了新的接口android.media.projection,使用该接口,第三方应用程序无需获取系统root权限也可以直接进行屏幕截图操作了。查询其官方api可知,该接口主要用来“屏幕截图”操作和“音频录制”操作,这里只讨论用于屏幕截图的功能。由于使用了媒体的映射技术手段,故截取的屏幕并不是真正的设备屏幕,而是截取的通过映射出来的“虚拟屏幕”。不过,因为截图我们希望的得到的肯定是一张图而已,而“映射”出来的图片与系统屏幕完全一致,所以,对于普通截屏操作,该方法完全可行。

一、使用方法

首先用参数MEDIA_-PROJECTION_SERVICE调 用Context.getSystemService(),得到MediaProjectionManager类别实例;
其次,调用 createScreenCaptureIntent ()得到一个Intent;再次,使用startActivityForResult()启动屏幕捕捉;
最后,将结果返回到 getMediaProjection()上,获取捕捉数据。

二、Demo案例

该demo中使用了service,且该服务需要获取到startActivityForResult中的结果数据,由于直接在service中调用startActivityForResult不现实,故在该demo中还是用了共享类数据模型。由于代码较多,下面只对关键步骤稍作说明。
1、关键步骤及代码

  1. 获取MediaProjectionManager类实例:
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
mMediaProjectionManager=MediaProjectionManager)getApplication().getSystemService(Context.MEDIA_PROJECTION_SERVICE);
        startIntent();
    }
  1. 利用MediaProjectionManager类实例的功能函数createScreenCaptureIntent()生成intent,为接下来的的抓取屏幕做准备
startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);
  1. 在onActivityResult()中获取resultCode和resultData,以方便下面getMediaProjection()使用。
public void onActivityResult(int requestCode, int resultCode, Intent data) {
	......
		result = resultCode;
     intent = data;
	......
 }
  1. 利用第一步中的MediaProjectionManager类实例,通过getMediaProjection()方法(参数分别为3中的resultCode和resultData)获取当前的屏幕映射,并保存到临时MediaProjection实例中。
public void setUpMediaProjection(){
        mResultData = ((ShotApplication)getApplication()).getIntent();
        mResultCode = ((ShotApplication)getApplication()).getResult();
        mMediaProjectionManager1 = ((ShotApplication)getApplication()).getMediaProjectionManager();
        mMediaProjection = mMediaProjectionManager1.getMediaProjection(mResultCode, mResultData);
        Log.i(TAG, "mMediaProjection defined");
    }
  1. 使用ImageReader实例通过getSurface()方法获取屏幕表层,使用4步中的MediaProjection临时实例通过createVirtualDisplay()方法进行虚拟屏幕的显示。
private void virtualDisplay(){
        mVirtualDisplay = mMediaProjection.createVirtualDisplay("screen-mirror",
                windowWidth, windowHeight, mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                mImageReader.getSurface(), null, null);
        Log.i(TAG, "virtual displayed");
    }
  1. 转化并保存截取的屏幕数据到文件(命名、后缀自定义,按照一般的图片格式就可以了,如:png等)
private void startCapture(){
        strDate = dateFormat.format(new java.util.Date());
        nameImage = pathImage+strDate+".png";

        Image image = mImageReader.acquireLatestImage();
        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);
        image.close();
        Log.i(TAG, "image data captured");
		   .......
    }

2、LOG对比

12-02 15:30:22.390 I/art     (19796): Late-enabling -Xcheck:jni
12-02 15:30:22.710 I/View    (19796): ssignParent(ViewParent parent) parent is: android.view.ViewRootImpl@1fc4ad78
12-02 15:30:22.900 I/OpenGLRenderer(19796): Initialized EGL, version 1.4
12-02 15:30:24.620 I/MainActivity(19796): user agree the application to capture screen
12-02 15:30:24.770 I/MainActivity(19796): start service Service1
12-02 15:30:24.930 I/View    (19796): ssignParent(ViewParent parent) parent is: android.view.ViewRootImpl@25532924
12-02 15:30:24.930 I/Service (19796): created the float sphere view
12-02 15:30:24.940 I/Service (19796): prepared the virtual environment
12-02 15:30:24.980 I/ash     ( 4267): view add pid:19796 uid:10161
12-02 15:30:24.980 I/AppsCleanUp( 4267): add top view, pid:19796 count:1
12-02 15:30:25.630 I/View    (19796): ssignParent(ViewParent parent) parent is: null
12-02 15:30:25.660 I/View    (19796): ssignParent(ViewParent parent) parent is: null
12-02 15:30:30.160 I/Service (19796): start screen capture intent
12-02 15:30:30.160 I/Service (19796): want to build mediaprojection and display virtual
12-02 15:30:30.170 I/Service (19796): mMediaProjection defined
12-02 15:30:30.190 I/Service (19796): virtual displayed
12-02 15:30:31.200 I/Service (19796): image data captured
12-02 15:30:31.200 I/Service (19796): image file created
12-02 15:30:32.020 I/Service (19796): screen image saved
12-02 15:30:40.340 I/PgedBinderListener( 4267): kstate callback type:8 value1=19796 value2=KILLED
12-02 15:30:40.570 I/MediaProcessHandler( 3417): processOp opType: 1, uid: 10161, pid: 19796
12-02 15:30:40.570 W/MediaProcessHandler( 3417): remove target not exist, maybe the UI process: uid: 10161, pid: 19796
12-02 15:30:40.570 I/HwSystemManager( 4203): HoldService:uid:10161 pid:19796 died
12-02 15:30:40.570 I/HwSystemManager( 4203): HoldService:oldVersionKey:10161,19796

3、附件及效果图
【附件】 CaptureScreen.rar
【效果图】
这里写图片描述 这里写图片描述
4、操作说明
安装完成并启动该apk后,会弹出一个对话框,点击“立即开始”,整个对话框会直接退出,然后会出现一个火苗洋时代的浮动小球。
需要截屏时,直接单击该小球即可。
默认保存的截图路径在:sdcard/Pictures路径下,
有的手机的图库查看器中设有默认文件夹的选择路径功能,如果在图库查看器中看不到截图,则可以直接通过“文件管理”到“本地/内部存储/Pictures”路径下查看。

三、总结

通过上面实例可以看出,该方法的使用主要工作就在截屏前的准备上。需要说明的是,其中有部分像素微调的代码,该微调操作是为了消除截图的黑边框(毕竟真正截的图是媒体的映射屏~~)。另外,如上文提到,该功能是在Android5.0才开始加入的(或者说开放出来的)新接口,所以,该方法只能用于5.0以上的Android版本(api21以上)。
另外,通过上述说明可以得出,整个阶段分为映射准备阶段和开始截屏并保存阶段。需要注意的是:准备阶段完成后需要等待1s或者更长时间才可以去操作截屏,否则有可能imageReader在进行newInstance时不成功!!(同事在移植该应用时遇到过,在此添加上)

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值