最近研究了一些Android
的截屏方法,做一个总结。
图片剪裁方法
使用View.getDrawingCache()
得到Bitmap
。非常简单但是只能截图本应用的图片,并且没办法控制截图的范围。
对Bitmap
进行截屏。可以方便的操作截取大小,但是需要提前截取整个屏幕,然后再处理生成的Bitmap
。截取屏幕流程:打开一个新的Window
全屏展示,上面包含一个CropView
->操作CropView
选择区域->根据选择区域截取Bitmap
。
代码调用
一般需求用来从相册选择或者从相册发送图片。
(1).选择图片。打开系统选择图片的Activity
并设置相应的参数。
(2).截切图片。选择图片成功(onActivityResult
)以后,设置相应参数开始裁剪图片。
(3).onActivityResult
处理裁剪图片成功以后回调。
流程分析(基于api25
)
系统从相册打开图片,然后截取图片。源码在com.android.gallery3d
包下,com.android.gallery3d
是一个单独的应用(进程)。
(1).打开相册com.android.gallery3d/.app.GalleryActivity
,选择图片,略过。
(2).打开截图界面com.android.gallery3d/.filtershow.crop.CropActivity
。
(3).执行BitmapIOTask.doInBackground
,对sourceUri
进行操作,保存在dstUri
中。
(4).执行CropActivity.getCroppedImage
进行截图。
api21
以上使用MediaProjectionManager
,MediaProjection
,VirtualDisplay
,ImageReader
。
代码调用
(1).申请权限
获取权限以后,获取MediaProjection
对象。
(2).获取屏幕内容
(3).截图,android.media.Image
可以拿到相应的Bitmap
信息。
|
|
利用ddmlib
(DDMS的ddmlib.jar
)截屏。
代码调用
(1).创建Java
工程,并导入ddmlib.jar
,ddms.jar
和ddmuilib.jar
。在Android SDK
的tools/lib
目录下面。
(2).直接完整代码了,注意getDevice
中的createBridge
需要改成自己电脑adb
的路径。takeScreenshot
中保存的路径需要设置成自己的路径。main
中调用的getDevice
需要改成自己手机的id。
|
|
adb shell screencap -p filepath
。使用adb截图保存在手机上面,没有具体分析调用流程。
执行adb shell input keyevent 120
,相当于调用的系统截屏(手机组合键截图)。
流程分析(基于api25
)
(1).按下组合键以后,执行到PhoneWindowManager.interceptKeyBeforeDispatching()
(2).执行到PhoneWindowManager.ScreenshotRunnable.run()
(3).执行到takeScreenshot
(4).com.android.systemui.screenshot.TakeScreenshotService
|
|
(5).截取全屏或者指定区域最后都是执行到了同一个逻辑。分析全屏截取。执行mScreenshot.takeScreenshot
。
|
|
(6).GlobalScreenshot.takeScreenshot
,截取选择区域也是这个方法。
(7).SurfaceControl.screenshot
,SurfaceControl
是hide
的。
|
|
扩展
截屏功能native
层通过framebuffer
来实现的(这里可能不是很准确)。帧缓冲(framebuffer
)是Linux
为显示设备提供的一个接口,把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。Android
系统是基于Linux
内核的,所以也存在framebuffer
这个设备,我们要实现截屏的话只要能获取到framebuffer
中的数据,然后把数据转换成图片就可以了,Android
中的framebuffer
数据是存放在 /dev/graphics/fb0
文件中的,所以我们只需要来获取这个文件的数据就可以得到当前屏幕的内容。
总结
在自身应用中截取一个View
比较简单。但是去截取任意界面任意大小,就需要比较大的权限,可能需要root
手机。直接使用android.view.SurfaceControl
反射处理。需要使用到系统的uid``android:sharedUserId="android.uid.systemui"
。api21
以后系统开放了截图的接口,可以直接使用(自己的使用的时候碰到过问题)。