一种实现(无须root)手机截屏方案
轉載請把頭部出處鏈接和尾部二維碼一起轉載,本文出自逆流的魚yuiop:http://blog.csdn.net/hejjunlin/article/details/53966818
前言:一年半多以前,我們曾有個項目,要做一個截屏功能,當時負責調研的同事,答應了產品上這個功能,但開發一周后,發現,無法實現截取手機屏幕圖像,須要root權限,才能做。因為最近研究MediaProjection,意外的發現,竟然無須root,可以輕松實現次功能。曾經被做不到的,如今做到了,很難相信此時的心情。看下今天的Agenda:
- Android源碼中使用組合鍵是如何實現屏幕截圖功能的?
- MediaProjection實現手機截屏效果
- 簡要思路
以我的魅族手機為例,是同時按電源鍵+音量下鍵來實現截屏,蘋果手機則是電源鍵 + HOME鍵,小米手機是菜單鍵+音量下鍵,而HTC一般是按住電源鍵再按左下角的“主頁”鍵。那么Android源碼中使用組合鍵是如何實現屏幕截圖功能呢?
Android源碼中對按鍵的捕獲位于文件PhoneWindowManager.java中?
?
這個類處理所有的按鍵事件,其中函數interceptKeyBeforeQueueing()會對常用的按鍵做特殊處理。以我自己魅族手機為例,是同時按電源鍵和音量下鍵來截屏,那么在這個方法中可以看到這么如下代碼:?
interceptKeyBeforeQueueing?
?
以上代碼總結為:當按下音量下鍵時,且是down時,會進入到interceptPowerKeyDown方法中,而按下電源鍵時,且是down時,也會進入interceptPowerKeyDown方法中,接著進入這個方法中看下:?
interceptPowerKeyDown?
?
以上代碼總結為:如果是interactive為true,且一些其他條件滿足時,會記錄電源鍵按下的時間,且進入interceptScreenshotChord方法中:?
?
以上代碼總結為:用兩個布爾變量判斷是否同時按了音量下鍵和電源鍵后,再計算兩個按鍵響應Down事件之間的時間差不超過( // Time to volume and power must be pressed within this interval of each other.?
private static final long SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS = 150;)150毫秒,也就認為是同時按了這兩個鍵后,此種case時,才是真正的屏幕截屏的組合鍵。
獲取到組合鍵后,會用一個mHandler 開始post一個runnable,進入這個runnable中:?
?
以上代碼,會執行方法takeScreenshot方法,接著進入:?
?
以上代碼總結為:使用AIDL綁定了service服務到”com.android.systemui.screenshot.TakeScreenshotService”,注意在service連接成功時,對message的msg.arg1和msg.arg2兩個參數的賦值。其中在mScreenshotTimeout中對服務service做了超時處理。接著我們找到實現這個服務service的類TakeScreenshotService,該類在(frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot包下):?
?
對應代碼如下:?
?
以上代碼總結為:引用了GlobalScreenshot類,調用了takeScreenshot方法,緊接著進入:?
?
以上代碼總結為:引用SurfaceControl類,調用了screenshot方法, 傳入了屏幕的寬和高,這兩個參數,接著進入SurfaceControl類中,位于frameworks/base/core/java/android/view目錄下:?
?
對應screenshot方法代碼如下:?
?
screenshot?
?
最終到達native方法中nativeScreenshot?
?
上面就是java層的部分,接著到jni層,在\frameworks\base\core\jni\android_view_SurfaceControl.cpp中:?
?
以上代碼總結為:到jni中,映射nativeScreenshot方法的是nativeScreenshotBitmap函數,這個函數代碼如下:?
?
?
以上代碼總結為:實例化ScreenshotClient類,且調用update方法,最終通過構造一個新的bitmap,把screenshot得到的像素點,及相關信息達到bitmap,通過GraphicsJNI,重新創建一個bitmap返回給上層。?
我們接著看下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干貨,源碼分析,歡迎關注我的微信公眾號,掃一掃下方二維碼或者長按識別二維碼,即可關注。
如果你覺得好,隨手點贊,也是對筆者的肯定,也可以分享此公眾號給你更多的人,原創不易 與50位技術專家面對面20年技術見證,附贈技術全景圖
總結
以上是生活随笔為你收集整理的一种实现(无须root)手机截屏方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VirtualAPK:滴滴 Androi
- 下一篇: Android后台杀死系列之二:Acti