天天看點

【源碼解讀】Screencap源碼分析-基礎篇

由于工作的關聯關系,本文将針對Android screencap的源碼進行分析,此文是基礎篇,為後續對源碼的修改做知識儲備。

本文期望達到的目的:

  1. 了解screencap使用
  2. 了解screencap實作基礎原理
  3. 為後續screencap源碼修改和其他應用做準備

源碼位置:

android4.0之後内置了截圖工具screencap,一般位于/system/bin/screencap

源碼路徑:frameworks/base/cmds/screencap/

使用介紹:

usage: screencap [-hp] [-d display-id] [FILENAME]
   -h: this message
   -p: save the file as a png.
   -d: specify the display id to capture, default 0.
If FILENAME ends with .png it will be saved as a png.
If FILENAME is not given, the results will be printed to stdout.
           

使用相當簡單:

adb shell screencap -p/sdcard/testscreen.png

備注:

  1. -d 參數為支援擴充的display裝置
  2. 如果檔案名以.png結尾時,它将儲存為png檔案,如果檔案名沒有給出,則結果被會被輸出到stdout(導出會相當的卡)

如果手機沒有連接配接adb,我們也可以用以下代碼去完成調用:

public Boolean DoScreenShot(){
	Process process = null;
	DataOutputStream os = null;
	try {
	}
	catch (Exception e){
		return false;
	}finally {
		Try {
			process = Runtime.getRuntime().exec(“su”);
			os = new DataOutputStream(process.getOutputStream());
			os.writeBytes(“serccncap –p ” + strFileFullPath);
			os.flush();
			process.waitFor();
			If (os != null) {
				os.close();
			}
			process.destroy();
		}
		catch (Exception e){
		}
	}
	return true;
}
           

源碼分析:

核心代碼:

【源碼解讀】Screencap源碼分析-基礎篇

整個程式最核心的代碼就是上圖。

擷取截圖資訊的流程是通過ScreenshotClient對象,如果發現ScreenshotClient擷取失敗則讀取/dev/graphics/fb0檔案。

不知道/dev/graphics/fb0是什麼檔案,可以google下。

1、 fb0讀取比較容易看懂:(如果有興趣的話可以繼續去看linux核心檔案中的顯示檔案)   

  1. 直接open()打開檔案
  2. 通過fb_var_screeninfo結構體來定位fb0的檔案頭格式
  3. 根據偏移位址把一貞的資料讀到一個mmap中,
  4. base = (void const *)((char const *)mapbase + offset);

2、ScreenshotClient:

  1. 建立ScreenshotClient screenshot
  2. 直接調用screenshot.update()
  3. base = screenshot.getPixels();

後續我們再繼續閱讀ScreenshotClient類,了解它是如何顯示螢幕顯示擷取的:

ScreenshotClient類位于frameworks/native/libs/gui/SurfaceComposerClient.cpp中。

這個類提供了updata()函數:

【源碼解讀】Screencap源碼分析-基礎篇

從567行來看,該函數是直接調用了SurfaceFlinger服務的代理對象中的captureScreen()函數。

其中getComposerService()函數的源碼:

【源碼解讀】Screencap源碼分析-基礎篇

從代碼上可以看出,instance.mComposerService是SurfaceFlinger服務的代理對象。

如果有興趣的話可以繼續跟進去裡面的代碼,會發現上面的第5行代碼就是通過getService("SurfaceFlinger", &mComposerService)擷取服務代理對象。

說到“SurfaceFlinger”服務,這個伺服器是比較龐大的架構。主要的作用是進行UI繪制。(後續可以深入分析)

是以:

    Screencap的實作就是通過通路“SurfaceFlinger”服務或者讀取fb0檔案進行截屏的。

最後我們看看Screencap儲存圖檔的代碼:

【源碼解讀】Screencap源碼分析-基礎篇

程式隻提供了png字尾的圖檔格式,如果想要傳回其他的圖檔格式,需要要進行類型type的修改。

圖檔格式:

1、SkImageEncoder 中的Type 枚舉(注意有些android版本是沒有完全實作的)

enum Type{
	kUnknown_Type,
	kBMP_Type,
	kGIF_Type,
	kICO_Type,
	kJPEG_Type,
	kPNG_Type,
	kWBMP_Type,
	kWEBP_Type,
	kKTX_Type,
};
           

2、kDefaultQuality預設值為80 圖檔的清晰度

如果想提高這個工具的截屏效率,可以通過選擇圖檔格式或者選擇圖檔的品質。

總結:

  1. 經實際測試,1s 不到10幀,screencap效率是相當差的(包含儲存為jpeg等格式)。
  2. 如果需要修改儲存圖檔的格式,需要修改源碼重新編譯。

繼續閱讀