ByteScope

am force-stop

會改變裝置

殺掉屬於某個 app 的所有行程與服務。

指令
adb shell am force-stop
指令
活動與 intent(am)
風險
會改變裝置

語法

am force-stop <package>

範例

  • am force-stop com.example

    在不解除安裝、也不清資料的前提下停掉一個亂跑的 app。

  • top -b -n 1 -m 10

    在停掉任何東西之前,先看清楚到底是誰在吃 CPU。

輸出怎麼讀

通常什麼都沒有。把沒有輸出當成成功,把任何 `Error:`、例外或權限相關的行當成失敗。想要證據的話,事後去 `top` 裡找找那個 app 的行程,而不是相信「沒有輸出」。

輸出欄位

欄位意義
(no output)指令執行了。
Error: …套件無法解析,或呼叫被拒絕。

需要什麼權限

不用 root、不用 `adb root`、不用 `su`:一條授權過的 `adb` 連線就是全部條件。`am force-stop` 打到的是 `ActivityManagerService.forceStopPackage`,它會檢查呼叫方有沒有 `android.Manifest.permission.FORCE_STOP_PACKAGES`,沒有就丟例外;而 Shell 套件的資訊清單(`packages/Shell/AndroidManifest.xml`)宣告的正是這個權限,這也是為什麼這個指令從纜線這端能用、從幾乎其他任何地方都不能用。同一份資訊清單也讓它的預設範圍比多數人以為的更廣。它同時持有 `INTERACT_ACROSS_USERS_FULL`,而 `runForceStop` 一開始就把 user id 設成 `UserHandle.USER_ALL`,只有在你傳 `--user` 時才收窄。所以只帶一個參數的那種寫法,並不是把 app 停在看著螢幕的那個人身上——它是一次把裝置上每個 Android 使用者裡的它停掉:工作設定檔、次要使用者、同一個 app 的複製版本。你只想停眼前這個使用者的話,就要寫出來。除此之外沒有任何東西擋在你和這次強制停止之間:沒有確認、沒有試跑、沒有可以撤銷的逐套件許可,而且平台不會攔你去 force-stop 系統 app。正式版上 `adb root` 本來就會被拒絕,而且在這裡也買不到任何東西,因為 shell 早就持有這個呼叫要的權限。

失敗訊息對照

訊息意義
Error: Unknown option: --user0`runForceStop` 只接受一個選項,就是 `--user`。其他任何東西都會被寫到標準錯誤,而且這個方法會在呼叫 `forceStopPackage` **之前**就回傳 -1,所以連在一起的 `--user0`、縮寫成 `-u`,或多打一個 `--all`,都會讓 app 繼續跑,而那行字讀起來像警告而不像拒絕。要看的是結束碼,不是那行字的語氣。
Exception occurred while executing 'force-stop':有 `Throwable` 逃出 `onCommand`;標準錯誤上是一行空白、這個橫幅與堆疊追蹤。`java.lang.IllegalArgumentException: Argument expected after "force-stop"` 代表沒給套件名稱。出現指名 `android.permission.FORCE_STOP_PACKAGES` 的 `java.lang.SecurityException`,代表這次呼叫不是來自 shell 或 root,而在纜線這一端通常表示你人在 `run-as` 裡面、已經不是 uid 2000 了。
Unknown command: force stop連字號是子指令名稱的一部分。被引號包住或被拆開時,`am` 看到的是 `force`,`handleDefaultCommands` 會把這行印在**標準輸出**並回傳 -1,一個行程都沒碰。它和一次成功的 force-stop 所產生的沉默完全不像,這是它唯一的優點。
cmd: Can't find service: activity`am` 是包在 `cmd activity` 外面的東西,而 `cmd` 在 service manager 裡找不到那個服務。什麼都沒停掉。開機期間、`system_server` 還沒註冊 `activity` 時本來就會這樣;它不代表你的套件名稱打錯了。

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

什麼都不會被刪,下次被啟動或被廣播喚醒時 app 就會再跑起來——但這不是唯讀操作:行程裡尚未儲存的狀態沒了,它排定的 alarm 與 job 也會被取消,直到它再次執行為止。沒有還原指令,解法是把 app 打開。

版本與廠商注意事項

  • 在依 CPU 排序的行程表上按 force-stop 很容易點錯,而失去未儲存狀態的往往是你本來不想動的那個 app。
  • 這和停用不同:app 隨時可以再跑起來。

這個我們有 UI

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

開啟 Android System Monitor

相關指令

和 am force-stop 會在同一次作業裡出現的指令。

指令做什麼
top一次性的行程表:CPU 占用、記憶體、狀態與指令列。
dumpsys meminfo哪些行程最吃記憶體,量的是 PSS(按比例分攤的用量)。
am start用元件或 intent 啟動 Activity——這是驗證 deep link 能不能被接走最快的方法。

其他活動與 intent(am)指令

指令做什麼
am broadcast送出廣播 intent,這是從 shell 驅動 SystemUI demo mode 等平台接收器的方法。

這些行為是在撰寫 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 執行這類指令。或者瀏覽全部指令。