
去年穀歌進行的一項內部調查顯示 Play Store 中超過 40% 的一星應用存在穩定性問題。另一方面,對於效能卓越的應用,人們打分和評論往往越來越好,這讓它們在 Google Play 中的排名上升,下載量也隨之增加。不僅如此,使用者參與度也更高,而且願意花更多的時間和金錢在這些應用上。
因此,解決應用穩定性問題能夠顯著影響到應用成功與否。
通過對應用質量的客觀測量,開發者能夠輕易發現應用亟待解決的穩定性問題,為此我們在 Google Play Console 新增了一款名為 Android vitals 的新板塊。藉助 Android vitals,開發者無須新增額外工具程式碼或者庫就能瞭解應用存在的效能及穩定性問題。當應用在大量裝置上執行時,Android vitals 會收集與應用效能相關的匿名資料。通過這種途徑獲得的資訊量是其他方式無法匹及的,即使是硬體實驗室測試也不行。
Android vitals 可以向開發者傳送以下三種警告:崩潰、應用程式無法響應以及渲染次數。這三種情況都會直接影響到使用者體驗以及他們對應用的評價。此外,使用者可能不會將 “異常耗電事件” 這類不良行為與您的應用直接聯絡起來。
這篇文章將探討其中以下兩個問題:
1.過度喚醒:過度喚醒會對電池壽命造成影響,而且在無法及時充電的情況下,可能導致使用者無法繼續使用裝置。此類行為可能會讓使用者迅速解除安裝您的應用;
2.應用程式無法響應 (ANR)事件:當應用的使用者介面卡住時候,此類事件會被觸發。在介面凍結時,若您的應用在前臺執行,會出現對話方塊提醒使用者 “關閉應用” 或者 “等待響應”。對使用者而言,此類行為和應用崩潰一樣糟糕。他們可能不會馬上解除安裝您的應用,但是如果 ANR 問題一直不解決,就很有可能會尋找其它替代應用。
過度喚醒

為了延長電池續航時間,螢幕關閉後,Android 裝置會禁用主 CPU 核心,進入深度睡眠模式。除非使用者喚醒裝置,裝置最好可以儘可能長地保持這種狀態。不過,在發生某些事件的情況下,還是很有必要喚醒 CPU 並向使用者發出警告 —— 比如說,鬧鐘觸發或者收到新訊息。開發者可以通過喚醒鬧鐘 (wakeup alarm) 來處理此類警告,不過也不一定非要這麼操作,在下文中會對此稍加解釋。到目前為止,喚醒看上去似乎是個不錯的東西,讓重要事情能引起使用者注意,不過要是喚醒太多次就適得其反,電池壽命也會大打折扣。
Android vitals 如何顯示過度喚醒
Android vitals 能夠幫助開發者瞭解自己的應用是否存在喚醒次數太多的問題。通過收集有關應用行為的匿名資料,Android vitals 可以顯示有多少比例的使用者在裝置滿電之後,每小時經歷 10 次以上的裝置喚醒。關鍵就是看有沒有紅色的圖示出現,若圖示出現,則說明應用已經越過了不良行為門檻,屬於 Google Play 中表現最次的一檔應用,而您則須要想辦法改善應用行為了。

除了喚醒鬧鐘,還有別的方法嗎?
開發者主要是通過 AlarmManager API 設定 RTC_WAKEUP 或 ELAPSED_REALTIME_WAKEUP 旗標,讓應用在特定時間或者在某一時間間隔後喚醒裝置。該功能須謹慎對待,僅在沒有其它更優的任務排程和通知機制的情況下才可使用。在使用喚醒鬧鐘的時候,您需要考慮以下幾點:
- 若您需要顯示資訊以響應來自網路的資料,考慮通過使用 Firebase Cloud Messaging 等工具來實現訊息推送。利用該機制而不是定期輪詢新資料,您的應用會僅在需要時才被喚醒。
- 如果您無法使用訊息推送並依賴定期輪詢,考慮使用 JobScheduler 或者 Firebase JobDispatcher (或者使用 SyncManager 來處理賬戶資料)。它們的 API 等級比 AlarmManager 高,而且在智慧任務排程方面具備以下優點:
-- 批量操作:批量操作任務而不是多次喚醒系統進行操作,這使裝置能更長時間處於睡眠狀態。
-- 標準:您可以明確任務執行須滿足的具體標準,如網路可用性或者電池充電狀態。設定標準能夠避免喚醒裝置以及不必要的應用執行。
-- 持續性以及自動退避 —— 繼續執行任務 (即使在重啟後) 並且在失敗的情況能自動重試。
-- 低耗電模式 (doze) 相容性 —— 僅在低耗電模式或者應用待機模式未設定任何限制的情況下,任務才能執行。
當且僅當訊息推送以及任務排程對您的任務不適用時,您才可以利用 AlarmManager 設定喚醒鬧鐘。換個角度來說就是,僅當您想要在特定時間觸發鬧鐘,不考慮網路以及其它情況,喚醒鬧鐘才是必要的。
當 Android vitals 顯示過度喚醒時,您應採取何種對策?
為了解決過度喚醒問題,您須要確認應用在什麼地方設定了喚醒鬧鐘,然後降低這些鬧鐘的觸發頻率。
那麼如何檢視應用在哪些地方設了喚醒鬧鐘呢?您可以開啟 Android Studio 中的 AlarmManager 類,右擊 RTC_WAKEUP 或者 ELAPSED_REALTIME_WAKEUP 域,選擇 "Find Usages (查詢使用)",然後您就能看到專案中所有使用到此類旗標的事件了。仔細檢視每一種事件,然後考慮能否改用更為智慧的任務排程機制。

若您認為使用喚醒鬧鐘無法避免,那麼如果您的鬧鐘標籤滿足以下要求,Play Console 可以提供更好的分析資料:
- 在鬧鐘標籤中包含包、類或者方法名稱。這也可以幫助您輕鬆確定在源中的哪些地方設定了鬧鐘;
- 不要使用 Class#getName() 給鬧鐘命名,因為 Proguard 會對此產生混淆。請使用硬編碼字串;
- 不要向鬧鐘標籤新增計數器或者其它唯一識別符號,因為系統可能會貴去掉這類標籤,而且無法將它們計入有效資料內。
應用程式無法響應

對使用者而言,ANR 就是指當他們試圖與應用進行互動時,但介面卡住的事件。介面卡屏幾秒後,會出現對話方塊讓使用者選擇繼續等待或者強行停止應用。
從開發者的角度來看,ANR 則是指應用執行的操作耗時過久,如磁碟或網路 I/O,導致主執行緒阻塞。主執行緒 (有時候也被稱為 UI 執行緒) 主要負責響應使用者事件以及每秒重新整理 60 次螢幕。因此很關鍵的一點將任何可能延時主執行緒工作的操作轉到後臺執行緒。
Android vitals 如何顯示應用程式無法響應?
Android vitals 能收集並利用應用 ANR 事件的匿名資料,提供多個級別的 ANR 具體報告。主介面上概述了您應用中 ARN 活動的概覽資訊,顯示使用者至少經歷一次 ANR 事件的日對話比重,並且提供前一天以及前 30 天的情況的單獨報告。同時也提供了不良行為門檻。



應用程式無法響應常見原因
如上文所述,當應用程式影響到主執行緒時,ANR 事件會被觸發,而導致這種阻塞現象的原因各有不一,較為常見的有:
- 在主執行緒上執行磁碟或者網路 I/O。這是迄今為止導致 ANR 的最常見原因。雖然大部分開發者認同不應該在主執行緒上進行讀寫磁碟或者網路,但是有時候我們就是忍不住這麼做。在理想情況下,從磁碟上讀取幾個位元組的資料並不會引發 ANR,但是這絕對不是什麼好主意。如果使用者的裝置快閃記憶體很慢,如果其它同時進行讀寫的應用已經對裝置造成了很大壓力,而您的應用還在排隊等著執行 “快速” 讀取操作, 這樣真的不夠明智,所以千萬別在主執行緒執行 I/O;
- 在主執行緒上執行長計算。那麼記憶體計算又是怎麼一回事呢?訪問時間長並不會對記憶體造成影響,較小的操作應該也沒什麼問題。但是如果您開始迴圈執行復雜計算並且處理大資料集,主執行緒就很容易發生阻塞了。您可以考慮重新調整百萬畫素大影象的體積,或者在解析大 HTML 文字塊後,再將文字顯示到 TextView 中。總的來說,還是讓應用在後臺執行此類操作比較合適;
- 向主執行緒另一程式同步呼叫 binder:與磁碟或網路操作相似,線上程間進行阻塞呼叫時,程式執行會被轉移到您無法控制的地方。如果說其它程式忙碌,該怎麼辦?如果須要訪問磁碟或者網路以響應您的請求,又該怎麼辦?此外,資料在轉移到其它程式前,須要經過打包 (parcel) 與解包 (unparcel) 兩個步驟,會消耗不少時間。因此,還是建議從後臺執行緒進行程式間呼叫;
- 使用同步:即使您將複雜操作轉移到後臺執行緒執行,依舊須要與主執行緒溝通以顯示計算結果。多執行緒程式設計不容易,並且在使用同步鎖的時候,很難保證不出現阻塞執行。在最糟糕的情況下,可能會出現死鎖問題,即不同執行緒相互卡死。最好不要自己設計同步,建議使用專門的解決方案,比如說 Handler,將不可變資料從後臺執行緒傳回主執行緒。
如何檢測應用程式無法響應原因
尋找觸發 ANR 的原因不容易,我們拿 URL 類舉個例子:
- 您想看到 URL#equals (判斷兩個 URL 是否相同的方法) 阻塞執行緒嗎?SharedPreferences 又怎麼處理呢?
- 如果您是在後臺讀取數值的話,您能在前臺呼叫 getSharedPreferences 嗎?
這兩種情況都很可能導致長時間阻塞操作。幸好我們有 StrictMode,不用再自己瞎猜是什麼原因導致 ARN 了。在除錯構建的時候,您可以使用這個工具捕捉主執行緒上的意外磁碟或網路訪問。
您可以在應用中使用 StrictMode#setThreadPolicy,自定義檢查項,包括磁碟和網路 I/O 以及您通過 StrictMode#noteSlowCall 在應用中觸發的慢呼叫。同時,您也可以自己選擇讓 StrictMode 通過何種方式告知已檢測到阻塞呼叫:應用崩潰、日誌記錄還是顯示對話方塊?您可參看 ThreadPolicy.Builder class 獲取進一步資訊。
一旦您消除主執行緒上的阻塞呼叫,請記得再上傳應用至 Play Store 前,關閉 StrictMode。
解決過度喚醒以及 ANR 問題能夠提升應用質量及穩定性,提高應用評分,獲取更多好評,最終增加下載量。使用 Android vitals 讓您輕鬆快速地瞭解應用中亟待解決的問題。發現並解決程式碼中的這些問題可能並不容易,但是您可以利用工具和技術有效地完成工作。
點選這裡您可檢視 Android 和 Google Play 相關內容資訊
