1. 程式人生 > >android 6.0的DozeMode低功耗模式 及 引起的程序保活問題(一)

android 6.0的DozeMode低功耗模式 及 引起的程序保活問題(一)

一、問題的來源

        由於Android的開放特性,加上國內app開發者的覺悟普遍不高的情況下,越來越多的app開始利用安卓的系統特性甚至可以稱為漏洞,故意讓app退出後仍然佔用大量的硬體資源。   越來越多的應用會在後臺執行時“假死”,即不進入真正的Sleep,而是不斷在後臺輪詢蒐集使用者行為或者保持某些長連結來保障資料的實時性。而Android系統自身並未出臺對應的策略來約束或者限制這類應用行為,當這類應用越來越多,就會導致使用者的Android裝置電量消耗越來越高、手機越來越燙、流量偷跑、話費超標等情況。 這種不好的使用者體驗慢慢積累總有一天會爆發,冰凍三尺非一日寒,直到某一天這類問題影響到系統口碑 而被某個 新型移動端操作超載的時將悔之晚已。 想想當年的諾基亞是何等風光,我上大學的第一款手機就是 若基亞5380,現在呢,被安卓幹趴了,google當然不希望這樣的事發生在安卓上。

二、解決方案

        google也已經意識到這個問題,並且從Android M(即6.0)開始引入 Doze Mode,中文翻譯為“打旽模式”,但專業術語的翻譯為“低電耗模式”和“應用待機模式”,android系統的這兩個模式都是採用  Doze Mode 來實現的。

        doze 這個單詞的中文翻譯為“打瞌睡”,“打瞌睡”的意思就是稍微休息一下,例如我們長時間工作時,可能會覺得疲倦,這時我們會趴在桌子上眯幾分鐘,但是我們的思想狀態是可以隨時進入工作狀態的,比如領導隨時可能給你安排一項緊急的工作,這時你可以立即進入工作狀態。
        那麼對於 安卓系統而言,系統在鎖屏後也會選擇一個合適的時機休息片刻,一旦使用者再次解鎖屏,系統又能立即進入工作狀態。

三、Doze Mode介紹

        關於Doze Mode的介紹,我們可以看看 android官網上的說明,連結:安卓官網Doze Mode,這可能需要翻牆。我從官網摘錄一下基本的概念供牆內的同學閱讀:

從 Android 6.0(API 級別 23)開始,Android 引入了兩個省電功能,可通過管理應用在裝置未連線至電源時的行為方式為使用者延長電池壽命。低電耗模式通過在裝置長時間處於閒置狀態時推遲應用的後臺 CPU 和網路 Activity 來減少電池消耗。應用待機模式可推遲使用者近期未與之互動的應用的後臺網路 Activity。

低電耗模式和應用待機模式管理在 Android 6.0 或更高版本上執行的所有應用的行為,無論它們是否特別針對 API 級別 23。 為確保使用者獲得最佳體驗,請在低電耗模式和應用待機模式下測試您的應用並對程式碼進行必要的調整。 

需要注意的幾點:
1、Doze Mode會限制後臺應用的cpu、網路。
     在安卓早期,當記憶體不夠用時,安卓系統會回收後臺應用的程序,這僅僅是從 記憶體佔用 層面來限制後臺應用。那麼現在 系統會進一步限制後臺應用的 cpu、網路

2、不論app的targetApi是否23(即安卓6.0),只要使用者的手機是基於android 6.0,那麼你的應用也將受到Doze Mode的限制。
      在早期的安卓版本適配時,可能由於時間關係,我們並不會立即適配6.0的許可權系統,但為了讓應用在6.0上也能正常執行,我們會將app的targetApi 設定5.0,這樣app在6.0上執行的效果和在5.0上完全一致。 但這套方式對於Doze Mode不再適用了,可能是google為了回快android新版本的更新速度吧。 畢竟國內開發者的大環境就這樣,總要有個鞭子放在腦殼驅動你,你才會去更新。

四、Doze Mode的兩個具體應用:低電耗模式、應用待機模式

   這兩個模式都是通過 Doze Mode 來實現,那麼至些我們可以認為 Doze Mode 只是一種技術手段的名詞。    
      a、低電耗模式:

      如果使用者裝置未插接電源、處於靜止狀態一段時間且螢幕關閉,裝置會進入低電耗模式。 在低電耗模式下,系統會嘗試通過限制應用對網路和 CPU 密集型服務的訪問來節省電量。 這還可以阻止應用訪問網路並推遲其作業、同步和標準鬧鈴。
系統會定期退出低電耗模式一會兒,好讓應用完成其已推遲的 Activity。在此維護時段內,系統會執行所有待定同步、作業和鬧鈴並允許應用訪問網路。
      b、應用待機模式:
      系統判定應用在使用者未主動使用的程序,都認為此程序處於空閒狀態。
當用戶將裝置插入電源時,系統將從待機狀態釋放應用,也就不會使用Doze Mode來限制後臺程序的硬體資源。

Doze和App Standby的區別:
Doze模式需要螢幕關閉(通常晚上睡覺或長時間螢幕關閉才會進入),而App Standby不需要螢幕關閉,App進入後臺一段時間也會受到連線網路等限制。

五、國內rom對Doze Mode做的修改

        以上所說的 Doze Mode 在 原生安卓系統中能完好的執行,而且google還規定所有google play中上架的應用:除非應用的核心功能受到不利影響,否則 Google Play 政策禁止應用請求直接豁免 Android 6.0+ 中的電源管理功能(低電耗模式和應用待機模式)。  這樣的規定肯定是有利於使用者體驗的。
        具體來說 Doze Mode實現了兩個功能,一個是“低電耗模式”,它能讓你的手機在很低功耗的情況下執行,設想你正處於戶外長途旅行中,不方便隨時給手機充電,這個低電耗模式就能起到作用了;另一個是“應用待機模式”,我正在使用應用A,然後切換主頁再開啟應用B,這時應用A還在後臺不斷的消耗電量。
       那麼對於國內的rom呢,國內的rom廠商都在這套doze mode的基礎加上了自己的定製。但功能上是大同小異的,只是稱呼和細節上有些差異。

        以 MIUI 8.2、android 6.0 為例,看看 小米 提供了哪些功能,  在小米手機的 “設定”->"電量和效能"-


5.1 小米對“低電耗模式”的改版
        這裡我們可以看到,小米將“低電耗模式”換了個名稱叫“省電優化”,另外在原生系統中,只要未插電源且靜止且鎖屏狀態,系統就能進入“低電耗模式”。然而小米系統卻是自己加了一個開關,將這個權力交給使用者選擇,你的手機你作主,個人覺得這也是小米系統優化的比較好的地方。

另外小米的“省電模式”裡還有個省電設定,如下圖,裡面有兩項:
一是鎖屏一段時間後斷開資料,即鎖屏一段時間後所有app將無法通過流量來通訊
二是鎖屏一段時間後清理記憶體,即鎖屏一段時間後清理使用者記憶體空間,即你的使用者程序都會被殺掉。 
      注:某些廠商第2點的處理可能略有不同,假設這樣的場景,你開啟一個音樂播放app,本來你是希望鎖屏後一直播放的,結果因為這項設定導致鎖屏1分鐘後就自己停止了播放,因為記憶體被清理了。 據我所知 華為meta10 就有這項保護處理,那麼這也是個程序保活的手段。

5.2 小米對“應用待機模式” 的改版
         在安卓原生 6.0系統中,系統預設對所有應用加了此限制,使用者無法修改,而小米又機制的將選擇權交給使用者了,畢竟“你的手機你作主”,我買的手機都不能按我的意願執行,我他媽還會買嗎?
        從下面3張圖可以看出,小米手機給出了三種級別,一是不開啟待機模式;二是所有應用都開啟此模式;三是針對每個應用自己去配置要不要開啟。

六、測試這些設定如何影響後臺程序

        顯然Doze Mode 的特性是限制後臺應用的存活的,在研究如何保活之前,我們先來看看 Doze Mode在不同場景下是如何 限制後臺程序的。
        以 小米手機 MIUI 8.2、android 6.0 為例,執行以下測試,並記錄測試結果。

  1、在手機設定裡將此應用設為 “快速嘗試凍結所有後臺應用”,並且沒有重啟手機:
      a、在充電時後臺應用鎖屏狀態能一直執行。
      b、在不充電並水平放置在桌面,且手機靜止不動時,鎖屏後 後臺應用 3-5秒後 停止執行,前臺應用 2-10秒後停止執行
      c、在不充電,並手動晃動手機時, 鎖屏後 後臺應用 3-5秒後 停止執行, 前臺應用2-6秒後停止執行

  2、在手機設定裡將此應用設為 “不限制後臺應用的功能”,並且沒有重啟手機: 注:此前應用的狀態是“禁止後臺執行”(這會不會有影響?)
      a、不充電並水平放置,且手機靜止不動時, 鎖屏後 後臺應用  5-10秒後停止執行  ,前臺應用 4秒後停止執行
      b、在不充電,並手動晃動手機時, 鎖屏後 後臺應用 5秒後 停止執行, 前臺應用2秒後停止執行

  3、在手機設定裡 “依照應用配置限制後臺應用”,同時對應用設定“無限制後臺應用”,並且沒有重啟手機:
      a、不充電並水平放置,且手機靜止不動時, 鎖屏後 後臺應用  5秒後停止執行  ,前臺應用 2秒後停止執行
      b、在不充電,並手動晃動手機時, 鎖屏後 後臺應用 4秒後 停止執行, 前臺應用3秒後停止執行

  4、在手機設定裡設為 “不限制後臺應用的功能”,並且重啟手機:
      a、不充電並水平放置,且手機靜止不動時, 鎖屏後 後臺應用  50秒後仍在執行,前臺應用 70秒後仍在執行
      b、在不充電,並手動晃動手機時, 鎖屏後 後臺應用 45秒後 仍在執行, 前臺應用30秒後仍在執行

  5、基於第4步後,將設定改為“快速嘗試凍結所有後臺應用”,同時自定義裡設定應用為“無限制”,並且沒有重啟手機
      a、不充電並水平放置,且手機靜止不動時, 鎖屏後 後臺應用  45秒後仍在執行,前臺應用 40秒後仍在執行
      b、在不充電,並手動晃動手機時, 鎖屏後 後臺應用 30秒後 仍在執行, 前臺應用40秒後仍在執行

  6、基於第5步後,在自定義裡設定應用為“MIUI智慧省電”,並且沒有重啟手機
      a、不充電並水平放置,且手機靜止不動時, 鎖屏後 後臺應用  55秒後仍在執行,前臺應用 60秒後仍在執行

  7、基於第6步後,在自定義裡設定應用為“限制後臺功能,但應用不會被關閉”,並且沒有重啟手機
      a、不充電並水平放置,且手機靜止不動時, 鎖屏後 後臺應用  40秒後仍在執行,前臺應用 60秒後仍在執行    

  8、基於第7步後,在自定義裡設定應用為“禁止後臺執行,應用會被關閉”,並且沒有重啟手機
      a、不充電並水平放置,且手機靜止不動時, 鎖屏後 後臺應用  40秒後仍在執行,前臺應用 40秒後仍在執行

  9、在第8步後,重啟手機,但不修改任何設定,即設定仍然是"快速嘗試凍結所有後臺應用"&"禁止後臺執行,應用會被關閉"
      a、不充電並水平放置,且手機靜止不動時, 鎖屏後 後臺應用  40秒後仍在執行,前臺應用 40秒後仍在執行

  10、手機設定:“鎖屏1分鐘後清理記憶體“
      a、不充電並水平放置,且手機靜止不動時, 鎖屏後 58秒 應用程序被殺,不僅是cpu被凍結

注:第7、8、9這三項的測試跟我們的結論是衝突的,猜測原因可能是小米手機在重啟手機後需要一段時間的初始化工作,導致我們的設定沒有生效。

通過以上驗證,我們得出結論:
1、小米系統的這些設定基本上有效。
2、有些可能要重啟手機,重啟完成後可能還需要等待一段時間才能生效,猜測是可能是系統層有檔案記錄的處理,重啟完成後初始化這些資料還需要一段時間。

七、doze mode 除錯技巧

        開發者如何測試自己的應用在 doze mode下會受到怎樣的影響,可以參考安卓官網: 在低電耗模式和應用待機模式下進行測試 。
         對國內開發者來說,廠商可能會修改原生安卓裡的規則,這時我們不防用模擬器 來測試 doze mode。 

        扯完模擬器的坑後,我們繼續正題,摘錄部分官網的步驟:

一、在低電耗模式下測試您的應用您可按以下步驟測試低電耗模式:

1.使用 Android 6.0(API 級別 23)或更高版本的系統映像配置硬體裝置或虛擬裝置。
2.將裝置連線到開發計算機並安裝應用
3.執行應用並使其保持活動狀態
4.關閉裝置螢幕。(應用保持活動狀態。)
5.通過執行以下命令強制系統在低電耗模式之間迴圈切換:
    $ adb shell dumpsys battery unplug 
    $ adb shell dumpsys deviceidle step
    您可能需要多次執行第二個命令。不斷地重複,直到裝置變為空閒狀態。
6.在重新啟用裝置後觀察應用的行為。確保應用在裝置退出低電耗模式時正常恢復。

二、在應用待機模式下測試您的應用,要在應用待機模式下測試您的應用,請執行以下操作:

1.使用 Android 6.0(API 級別 23)或更高版本的系統映像配置硬體裝置或虛擬裝置。
2.將裝置連線到開發計算機並安裝應用
3.執行應用並使其保持活動狀態
4.通過執行以下命令強制應用進入應用待機模式:
    $ adb shell dumpsys battery unplug 
    $ adb shell am set-inactive <packageName> true
5.使用以下命令模擬喚醒應用:
    $ adb shell am set-inactive <packageName> false 
    $ adb shell am get-inactive <packageName>
    觀察喚醒後的應用行為。確保應用從待機模式中正常恢復。 特別地,您應檢查應用的通知和後臺作業是否按預期繼續執行

adb shell dumpsys deviceidle -h        可以檢視dumpsys提供的除錯手段:
命令 輸出
-h 顯示幫助選單
step 每次呼叫就會進入一次stepIdleStateLocked,從而實現狀態機的狀態逐步遷移
force-idle 讓Android裝置忽視Screen狀態以及Cable連線狀態,直接開始一輪Doze模式倒計時
disable 取消對Doze模式的支援
enable 恢復對Doze模式的支援
enabled 若支援Doze模式,返回1;否則返回0
whitelist 終端列印當前的白名單
whitelist +/-[xxxxxx] 將xxxxxx包加入/移出白名單
tempwhitelist [xxxxxx] 將xxxxxx包加入臨時白名單

八、保活的問題

        基於以上現象,如何在使用者沒有主動設定後臺保活的情況下(即沒有設定 不限制後臺活動),提供應用後臺存活的概率? 
理論上使用者沒有主動設定的情況下,都會遵守系統的 “應用待機模式”的管理規則,但在此規則之上是否還有其它手段 來讓 後臺程序 儘可能的保活, 有時間再單獨開一篇文章來研究.
        在安卓6.0及以後系統上,可以防止 doze mode 讓應用程序 程序阻塞掛起狀態 的保活方法:
 1、啟動前臺Service
 2、自定義鎖屏
 3、在應用內播放一段無聲的音樂
          這個方法,實測很有效,系統認為如果你的應用程序在鎖屏時存在能被使用者感知到的行為,那麼系統不會阻塞這個程序,播放音樂就是一個能被使用者感覺的行為,只不過我們巧妙的迴圈播放一個無聲音樂來欺騙 系統。
          缺點:可能會多消耗一定的電量,但實測並沒有多消耗很多, 半個小時也就消耗了2%電量, 實際就算不播放無聲音樂半小時系統也會存在一定的消耗。 所以暫時不需要考慮這方面。
 4、保持螢幕長亮(只在某些特殊功能場景下適用,如導航軟體)。系統不鎖屏,就不會進入doze mode