screencap
會改變裝置擷取螢幕畫面,可以寫進手機裡的檔案,也可以把 PNG 送到標準輸出。
- 指令
- adb shell screencap
- 指令
- 畫面與擷取
- 風險
- 會改變裝置
語法
screencap [-p] [<device path>]旗標與參數
| token | 意義 |
|---|---|
| -p | 編成 PNG。既沒有它、路徑也不是 .png 結尾時,輸出是原始的 framebuffer 內容。 |
| <path> | 寫進裝置上的檔案而不是標準輸出。 |
範例
screencap -p /sdcard/shot.png先寫到手機上,再把檔案拉回來。永遠安全。
screencap -p把 PNG 位元組送到標準輸出。從桌面要用 `adb exec-out`,絕對不要用 `adb shell`。
輸出怎麼讀
PNG 的二進位資料,或者你指定了路徑時什麼都沒有。沒有成功訊息:證據是那個檔案,或那串位元組。
輸出欄位
| 欄位 | 意義 |
|---|---|
| (PNG bytes) | 沒指定路徑時,送到標準輸出的圖片。 |
| (no output) | 圖片寫進你指定的檔案了。 |
需要什麼權限
不用 root,也不用 `adb root`。`screencap` 是原生執行檔而不是 `cmd` 的包裝,權限決定發生在 SurfaceFlinger 裡面:`validateScreenshotPermissions` 會放行 `AID_GRAPHICS`、`AID_SYSTEM`,或持有 `android.permission.READ_FRAME_BUFFER` 的呼叫者——而 `shell` 使用者所屬的套件 `com.android.shell`,在自己的資訊清單裡就有那個權限。依 display id 擷取的那條路更直接,`AID_ROOT`、`AID_GRAPHICS`、`AID_SYSTEM` 與 `AID_SHELL` 一律放行。所以一條已授權的線就夠了,除了 USB 偵錯本身,開發者選項裡沒有東西在管它。你明顯沒有的那個權限是 `android.permission.CAPTURE_BLACKOUT_CONTENT`,也就是能讓安全圖層進入擷取結果的那一個,而 `screencap` 從來不去要它——這才是受保護的影片會變成一塊黑色長方形的機制,不是編碼器壞了。再來是存取問題裡完全不屬於 Android 的那一半:寫進某個路徑就是以 `shell` 使用者做一次普通的 `open`,帶著 `O_WRONLY | O_CREAT | O_TRUNC` 與 0664 模式,所以共用儲存空間可以、app 的私有目錄不行,而你拿回來的拒絕訊息是一句樸素的 C `errno` 字串,不是平台幫它包裝過的東西。
失敗訊息對照
| 訊息 | 意義 |
|---|---|
| Error opening file: /data/data/com.example/shot.png (Permission denied) | `open` 失敗了,括號裡的文字是 C 函式庫的 `strerror`。`shell` 使用者不能寫進另一個 app 的資料目錄;寫到共用儲存空間再把檔案拉回來。同一個位置出現 `Read-only file system` 代表那個路徑在唯讀掛載的分割區上,不是截圖失敗。 |
| Ambiguous file type | 格式被講了兩次,而且兩次講的不一樣——`-p` 說是 PNG,`.jpg` 或 `.jpeg` 的檔名說是 JPEG。`screencap` 不會替你挑一個,它直接拒絕、以 1 結束,什麼都不寫。副檔名已經帶了格式時,就把 `-p` 拿掉。 |
| Failed to take screenshot. Status: -1 | 擷取請求回來時帶著錯誤狀態,所以沒有任何東西被編碼。SurfaceFlinger 的拒絕會在客戶端這一側浮現成這一行——真正的理由記在那邊而不是印在這裡,所以要去看同一時刻前後的 `logcat`。 |
| Failed to get ID for any displays. | SurfaceFlinger 回報沒有實體顯示器可以擷取。開機還沒完成時,以及真的沒有顯示器的 headless 版本上,這是預期中的。它不是權限問題,所以用更高的權限重跑不會有任何改變。 |
| Error writing to stdout. (Bad file descriptor) | 沒給檔名時,`screencap` 會複製自己的標準輸出來寫入,而那次複製失敗了。實務上這代表串流在它底下被關掉了——管線的讀取端提早離開是最常見的原因。沒有產出任何可用的東西;讀取端已經拿到的是一張被截斷的圖。 |
| [Warning] Multiple displays were found, but no display id was specified! | 這不是失敗,而它值得列出來的原因正是如此:擷取成功了,但在摺疊機或影像輸出接了東西的手機上,它挑中的那個顯示器不保證下次還是同一個。後面那兩行會告訴你要給 `-d`,以及去哪裡找合法的 id。在這種裝置上沒帶 `-d` 的無人看管擷取,請當成不可靠而不是錯誤。 |
會改變什麼,以及怎麼回頭
不帶路徑時,它把 PNG 位元組寫到標準輸出,裝置上什麼都不會改變。帶了裝置路徑,它會建立那個檔案,檔案已經存在就直接覆寫並截斷,所以路徑打錯可能蓋掉一個真的檔案,舊內容就沒了——Android 沒有垃圾桶。檔案也會一直占著儲存空間,回頭的路是 `rm <路徑>`;先把它拉到電腦上,截圖才算真的留下來。
版本與廠商注意事項
- 要串流那串位元組需要 `adb exec-out screencap -p`,不是 `adb shell`。單純的 shell 服務會給指令一個終端機,而終端機會把每個 0x0A 位元組變成 0x0D 0x0A——這會以一種在有東西試著解碼之前完全看不出來的方式毀掉 PNG。
- 受 DRM 保護的內容會是全黑。合成器拒絕交出那些緩衝區,所以這是平台的性質而不是擷取的問題。
- 想要一張不含你自己通知圖示的截圖,先用 `am broadcast` 把 SystemUI 切進 demo mode。
這個我們有 UI
Android ADB Toolbox 會透過 USB 執行這個指令並解析回應——只要 Chromium 系瀏覽器,不必安裝 SDK,也不會上傳任何東西。
相關指令
和 screencap 會在同一次作業裡出現的指令。
| 指令 | 做什麼 |
|---|---|
| screenrecord | 把螢幕錄成裝置上的一個 MP4 檔。 |
| am broadcast | 送出廣播 intent,這是從 shell 驅動 SystemUI demo mode 等平台接收器的方法。 |
| wm size | 回報顯示器的解析度,或覆寫它,讓手機假裝自己是另一塊螢幕。 |
其他畫面與擷取指令
| 指令 | 做什麼 |
|---|---|
| wm density | 回報顯示密度,或覆寫它——真正會讓版面斷點移動的是這一個。 |
這些行為是在撰寫 Web ADB 工具背後的解析程式時,對照 AOSP、toybox 與核心原始碼查證的。真的會因 Android 版本或廠牌而異的地方,這一頁會直接說明,而不是給你一個在半數手機上都是錯的數字。