ByteScope

wm density

會改變裝置

回報顯示密度,或覆寫它——真正會讓版面斷點移動的是這一個。

指令
adb shell wm density
指令
畫面與擷取
風險
會改變裝置

語法

wm density [<dpi> | reset]

旗標與參數

token意義
<dpi>要回報的密度。Android 自己的級距是 120 到 640,廠商也會出貨中間的值。
reset拿掉覆寫,回到實體密度。

範例

  • wm density

    只回報。

  • wm density 420

    搭配 `wm size 1080x2340` 就是一台寬 411 dp 的手機,這是真實存在的斷點。

  • wm density reset

    還原。

輸出怎麼讀

輸出範例

Physical density: 560
Override density: 420

形狀和 `wm size` 一樣:Physical 永遠有,Override 只在設了覆寫時才有。決定你拿到哪個版面的是 dp,所以真正有意義的量是「寬度像素乘 160 再除以這個 dpi」——420 dpi 的 1080 px 是 411 dp,同樣的 1080 px 換成 320 dpi 就變成 540 dp 的平板型版面。

輸出欄位

欄位意義
Physical density面板真正的 dpi。
Override density改成回報的值。只在覆寫存在時出現。

需要什麼權限

不用 root,也不用 `adb root`。`wm` 是一行的 shell 腳本,跑的是 `cmd window "$@"`(AOSP 的 `cmds/wm/wm.sh`),而覆寫會經過的 `setForcedDisplayDensityForUser` 與 `clearForcedDisplayDensityForUser` 都標註了 `@EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)`——這個權限 `com.android.shell` 在自己的資訊清單裡就有,所以一條已授權的線就夠了,除了 USB 偵錯以外也沒有其他開發者選項的開關牽涉在內。有一個不對稱值得帶著走:密度那組呼叫吃一個使用者 id,而 shell 指令傳的是 `USER_CURRENT`,尺寸那組則完全不吃使用者。所以密度覆寫是記在當下在前景的那個人身上的,一台有第二個使用者或工作設定檔的手機,可能在其中一邊看得到覆寫、另一邊看不到;尺寸覆寫沒有這個維度。另外一半是界限而不是權限。shell 指令在呼叫 window manager 之前就會拒絕任何小於 72 的值,而且它完全沒有上限——本站強制的範圍是它自己的理智檢查而不是平台的,因為平台會很開心地接受一個讓螢幕上只剩一個半按鈕的密度。

失敗訊息對照

訊息意義
Error: density must be >= 72下限是在 `runDisplayDensity` 自己裡面檢查的,在呼叫 window manager 之前,所以什麼都沒改。要注意這個檢查「沒有」出現在哪:沒有對應的上限,所以手滑打出來的 `4200` 會被接受並套用。實驗之前先把 `wm density reset` 放在畫面上。
Error: bad number後面會接上例外自己的 `toString()`,指出失敗的那個 token。和 `wm size` 不同,這個參數是當成純整數解析的——這裡沒有 `dp` 或 `px` 後綴的處理——所以 `420dpi` 是語法錯誤,不是一種聰明的 420 寫法。
Error: the uniqueId is invalid `-u <uniqueId>` 的形式拿到一個 display manager 不認識的 id,指令沒讀也沒設就回來了。行尾那個多出來的空白是上游自己的,所以請比對文字而不是整行。
Remote exception:回報那條路徑聯絡不到 window manager,於是印了這行加例外而不是一個密度。把它當成「未知」,絕對不要當成 0——把 0 dpi 帶進 dp 的計算會除以零,而這就是一次失敗的讀取如何讓整頁顯示探測結果變成胡言亂語。
cmd: Can't find service: window`cmd` 找不到 `window` 服務可以把子指令交出去,因為 `wm.sh` 只是 `cmd window` 的包裝。裝置還在開機、`system_server` 還沒登記它的時候,這是預期中的——等開機完成,不要斷定這台顯示器沒有密度。

會改變什麼,以及怎麼回頭

和尺寸覆寫一樣,它會跨過重新開機留下來,並改變手機上每個介面元素的縮放,設定 app 也包含在內。在手機面板上把密度設成 800,畫面上大概只看得到一個半按鈕,而你沒辦法靠點擊脫身。`wm density reset` 是回頭的路,在實驗之前先放在手邊。

版本與廠商注意事項

  • 只改密度、或只改尺寸,會做出一個不存在的裝置形狀。兩個都要設。
  • 0 與大得離譜的值不是測螢幕的方法,是讓螢幕變得看不懂的方法。

這個我們有 UI

Android ADB Toolbox 會透過 USB 執行這個指令並解析回應——只要 Chromium 系瀏覽器,不必安裝 SDK,也不會上傳任何東西。

開啟 Android ADB Toolbox

相關指令

和 wm density 會在同一次作業裡出現的指令。

指令做什麼
wm size回報顯示器的解析度,或覆寫它,讓手機假裝自己是另一塊螢幕。
screencap擷取螢幕畫面,可以寫進手機裡的檔案,也可以把 PNG 送到標準輸出。
settings put把值寫進設定提供者——多數開發者選項小技巧背後的機制。

其他畫面與擷取指令

指令做什麼
screenrecord把螢幕錄成裝置上的一個 MP4 檔。

這些行為是在撰寫 Web ADB 工具背後的解析程式時,對照 AOSP、toybox 與核心原始碼查證的。真的會因 Android 版本或廠牌而異的地方,這一頁會直接說明,而不是給你一個在半數手機上都是錯的數字。

常見問題

adb shell 指令要怎麼執行?

在手機上打開開發者選項與 USB 除錯、接上線,然後從裝了 platform-tools 的機器執行 adb shell <指令>——或者只執行 adb shell 取得互動提示字元,再直接打指令、不用前綴。如果指令會輸出二進位資料(例如截圖),請改用 adb exec-out 而不是 adb shell:單純的 shell 服務會配一個終端機,而它會改寫換行位元組並毀掉資料。

adb shell 指令需要 root 嗎?

這份參考裡的都不用。它們以 shell 使用者身分執行,可以針對自己的 Android 使用者管理套件、讀取大多數 dumpsys 服務、寫入設定提供者、注入輸入,以及讀取共用儲存空間。需要 root 的是這份參考刻意避開的事:讀取其他 app 的私有資料(除了對可除錯版本使用 run-as)、寫入系統分割區,或直接讀 /data/anr。

為什麼在我的手機上輸出看起來不一樣?

不是 Android 版本就是廠商。有些旗標與欄位是從特定版本才有的,那些都在各指令頁註明了。其餘是廠商改動,而受影響最深的是 dumpsys:一份 dump 是除錯輸出,它的文字就是那個服務的作者最後印了什麼,而廠商既會改它也會把改動往回移植。請用名稱而不是位置來讀欄位,並把沒見過的格式當成差異而不是失敗。

可以用瀏覽器而不是終端機來跑嗎?

很多指令可以——本站的 Web ADB 工具從 Chromium 系桌面瀏覽器透過 WebUSB 和手機對話,不必安裝 SDK,也不會上傳任何東西。每一頁在有對應工具時都會連過去。有兩個限制值得知道:同一時間只有一個程式能持有 ADB 介面,所以桌面的 adb server 要先停掉;而 adb forward 與 adb reverse 在瀏覽器裡完全無法運作。

比較想按而不是打字?Web ADB Toolkit 會從瀏覽器透過 USB 執行這類指令。或者瀏覽全部指令。