前言:一年半多以前,我们曾有个项目,要做一个截屏功能,当时负责调研的同事,答应了产品上这个功能,但开发一周后,发现,无法实现截取手机屏幕图像,须要root权限,才能做。因为最近研究MediaProjection,意外的发现,竟然无须root,可以轻松实现次功能。曾经被做不到的,如今做到了,很难相信此时的心情。看下今天的Agenda:
-
Android源码中使用组合键是如何实现屏幕截图功能的?
-
MediaProjection实现手机截屏效果
-
简要思路
以我的魅族手机为例,是同时按电源键+音量下键来实现截屏,苹果手机则是电源键 + HOME键,小米手机是菜单键+音量下键,而HTC一般是按住电源键再按左下角的“主页”键。那么Android源码中使用组合键是如何实现屏幕截图功能呢?
Android源码中对按键的捕获位于文件PhoneWindowManager.Java中
interceptKeyBeforeQueueing
interceptPowerKeyDown
private static final long SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS = 150;)150毫秒,也就认为是同时按了这两个键后,此种case时,才是真正的屏幕截屏的组合键。
获取到组合键后,会用一个mHandler 开始post一个runnable,进入这个runnable中:
screenshot
我们接着看下ScreenshotClient的声明,位于SurfaceComposerClient.h中。注意这个类是在frameworks\native\include\gui中,而前面android_view_SurfaceControl是在\frameworks\base\core\jni\下
最后辗转来到c++层,就是\frameworks\native\libs\gui下的SurfaceComposerClient.cpp中,实现ScreenshotClient声明的函数update,如下: 以上代码总结为:通过ISurfaceComposer接口,调用captureScreen函数,我们找到其对应的实现类\frameworks\native\libs\gui\ISurfaceComposer.cpp,找到captureScreen函数的实现如下: 以上代码总结为:把IGraphicBufferProducer,作为binder开始写到parcel中,最后一版看到transact,就知道当前是client端,而server端的onTransact()函数,会接收到传过来的参数。如这里的data。接着看下BnSurfaceComposer的onTransact方法,这个BnSurfaceComposer依然是在ISurfaceComposer.cpp中。 以上代码总结为:当进入到CAPTURE_SCREEN中,data会读取IGraphicBufferProducer生成出的图像buffe,接着调用 reply->writeInt32(res);返回给client.然后再回调到java层。以上就是系统截屏的原理。那对于多媒体这块可以通过MediaProjection+VirturalDisplay+MediaProjectionManager来实现截屏,以下我的实例效果图:
效果图1:操作过程,点击浮动小剪刀,就可以实现截屏,拖动小剪刀,可到任意位置:
效果图2:截图后过程
主界面:
体验apk:
链接:http://pan.baidu.com/s/1kVugxCV 密码:97z0
实现思路:
-
MediaProjection是一个token,用户可以授予应用程序捕获屏幕内容和录制系统音频。
-
屏幕截取,可以通过MediaProjectionManager的createScreenCaptureIntent,创建一个intent,保证有足够能力截取屏幕上的内容,但是没有系统声音。
-
调用setUpMediaProjection()方法获取共享数据并对其进行赋值;若已初始化,则直接调用virtualDisplay()方法利用之前定义的变量对截屏环境进行初始化。
第一时间获得博客更新提醒,以及更多android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。