后臺任務(wù)及省電優(yōu)化

前言

當(dāng)我們的App正在與用戶頻繁交互時,需要處理某個耗時任務(wù),而任務(wù)的結(jié)果需要立即反饋。這時因為主線程用于處理UI和用戶交互邏輯,如果有太多的耗時的邏輯在主線程中執(zhí)行,就會阻塞主線程,引發(fā)ANR異常,導(dǎo)致APK卡頓甚至崩潰。因此需要一個后臺線程來處理耗時任務(wù)。

當(dāng)我們的App并未與用戶頻繁交互,但是,App本身需要周期性的從服務(wù)器同步數(shù)據(jù)或者獲取數(shù)據(jù)(常見的心跳連接、收發(fā)消息長連接)。這時需要一個后臺進(jìn)程來處理,因為僅僅是后臺線程,不能保證長久存活,也不能保證任務(wù)能執(zhí)行完畢。

關(guān)于進(jìn)程?;畹奈恼掠泻芏嗔?,推薦幾篇不錯的文章:
騰訊——張興華 原文鏈接找不到了,這是別人轉(zhuǎn)發(fā)
關(guān)于 Android 進(jìn)程?;睿闼枰赖囊磺?/a>

但是,官方說明自己看:

Note: You should only use a foreground service for tasks the user expects the system to execute immediately or without interruption. Such cases include uploading a photo to social media, or playing music even while the music-player app is not in the foreground. You should not start a foreground service simply to prevent the system from determining that your app is idle.

本文的目的在于參考官方文檔,解決后臺進(jìn)程處理的相關(guān)問題,未必要保活。

一、低電耗(Doze)模式

Doze會通過推遲應(yīng)用程序的后臺CPU和網(wǎng)絡(luò)活動來減少電池消耗。

系統(tǒng)會定期退出Doze一段時間,讓應(yīng)用程序完成延期活動。在此維護窗口期間,系統(tǒng)將運行所有掛起的同步,作業(yè)和警報,并允許應(yīng)用程序訪問網(wǎng)絡(luò)。

在每個維護窗口結(jié)束時,系統(tǒng)再次進(jìn)入Doze,暫停網(wǎng)絡(luò)訪問并推遲作業(yè),同步和警報。隨著時間的推移,系統(tǒng)會越來越少地安排維護窗口,有助于在設(shè)備未連接到充電器時長時間不活動時減少電池消耗。

一旦用戶通過移動設(shè)備,打開屏幕或連接充電器喚醒設(shè)備,系統(tǒng)退出Doze并且所有應(yīng)用程序恢復(fù)正?;顒?。

功能限制

在低電耗模式下,您的應(yīng)用會受到以下限制:

  • 暫停訪問網(wǎng)絡(luò)。
  • 系統(tǒng)忽略喚醒鎖定。
  • 標(biāo)準(zhǔn) AlarmManager 鬧鐘(包括 setExact()setWindow())推遲到下一個維護期。
  • 如果您需要設(shè)置在設(shè)備處于低電耗模式時觸發(fā)的鬧鐘,請使用 setAndAllowWhileIdle()setExactAndAllowWhileIdle(),但9分鐘內(nèi)只能觸發(fā)一次鬧鐘。使用 setAlarmClock() 設(shè)置的鬧鐘將繼續(xù)正常觸發(fā),系統(tǒng)會在這些鬧鐘觸發(fā)之前不久退出低電耗模式。
  • 系統(tǒng)不執(zhí)行 WLAN 掃描。
  • 系統(tǒng)不允許運行同步適配器。
  • 系統(tǒng)不允許運行 JobScheduler。

adb指令強制進(jìn)入Doze模式

$ adb shell dumpsys battery unplug
$ adb shell dumpsys deviceidle step  切換到下一個狀態(tài),idle和active狀態(tài)之間切換
$ adb shell dumpsys deviceidle -h 查看幫助
$ adb shell dumpsys deviceidle force-idle [light|deep]  強制進(jìn)入idle狀態(tài)
$ adb shell dumpsys deviceidle force-inactive  強制進(jìn)入inactive狀態(tài)
$ adb shell dumpsys deviceidle unforce  強制解除idle、inactive狀態(tài),進(jìn)入active狀態(tài)
$ adb shell dumpsys deviceidle get [light|deep|force|screen|charging|network] 獲取相應(yīng)的當(dāng)前狀態(tài)
$ adb shell dumpsys battery reset 復(fù)位默認(rèn)值,恢復(fù)設(shè)備激活狀態(tài)。

二、應(yīng)用待機模式(App Standby)

App Standby推遲用戶最近未與之交互的應(yīng)用的后臺網(wǎng)絡(luò)活動。

adb指令強制進(jìn)入standby模式

$ adb shell dumpsys battery unplug
$ adb shell am set-inactive <packageName> true 進(jìn)入standby 模式
$ adb shell am set-inactive <packageName> false 退出standby模式,恢復(fù)到激活狀態(tài)
$ adb shell am get-inactive <packageName> 獲取當(dāng)前狀態(tài)
$ adb shell dumpsys battery reset 復(fù)位默認(rèn)值。

三、Android不同版本對省電的優(yōu)化

為了最大化電池并強制執(zhí)行良好的應(yīng)用行為,當(dāng)用戶看不到應(yīng)用(或前臺服務(wù)通知)時,Android會限制后臺工作。

模擬App在后臺運行的環(huán)境:
adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow

Android 6.0(API級別23)

引入了Doze模式和App Standby管理。當(dāng)屏幕關(guān)閉且設(shè)備靜止時,Doze模式會限制應(yīng)用程序行為。應(yīng)用程序待機將未使用的應(yīng)用程序置于特殊狀態(tài),限制其網(wǎng)絡(luò)訪問,作業(yè)和同步。

Doze60.png

Android 7.0(API級別24)

限制了隱式廣播并引入了 Doze-on-the-Go(縮短了進(jìn)入Doze模式的時間,相關(guān)限制分時分優(yōu)先級進(jìn)行)。

三個被限制的隱式廣播:
CONNECTIVITY_ACTION
ACTION_NEW_PICTURE
ACTION_NEW_VIDEO

Doze70.png

Android 8.0(API級別26)

進(jìn)一步限制了后臺行為,例如在后臺獲取位置和釋放緩存的喚醒鎖。

限制后臺進(jìn)程:應(yīng)用在前臺時,可以隨意創(chuàng)建前臺和后臺服務(wù);應(yīng)用剛剛進(jìn)入后臺時擁有一個幾分鐘的窗口,此時也可以隨意創(chuàng)建前臺和后臺服務(wù);進(jìn)入idle狀態(tài)時,系統(tǒng)會終止應(yīng)用的后臺服務(wù)。

多數(shù)情況下JobScheduler(官方Demo)可以取代后臺服務(wù),JobIntentService取代IntentService。

后臺應(yīng)用不能通過startService()啟動服務(wù)了,只能通過startForegroundService()方法啟動服務(wù),然后應(yīng)用有5秒鐘時間調(diào)用startForground()方法將服務(wù)置為前臺服務(wù),如果規(guī)定時間內(nèi)沒有調(diào)用該方法,app就會拋出 ANR 異常。

不能注冊隱式廣播接收者了(所謂隱式廣播就是沒有指定目標(biāo)應(yīng)用的廣播,哪個應(yīng)用都可以接收這樣的廣播)。
依然能注冊顯式廣播。
不再有需要簽名權(quán)限的廣播,因為以后廣播只會發(fā)送給簽名相同的app了。

Android 9(API級別28)

引入了 App Standby Buckets,其中應(yīng)用程序?qū)Y源的請求根據(jù)應(yīng)用程序使用模式進(jìn)行動態(tài)優(yōu)先級排序,共有5個級別。

Active: 正在被使用的或最近常被使用的App
Working Set: 周期性頻繁被使用的App
Frequent: 常被使用,但不是每天都用的App
Rare: 很少被使用的App
Never: 安裝后未被使用過的App

四、針對省電優(yōu)化的解決方案

對于解決如何確保應(yīng)用能優(yōu)雅地處理后臺任務(wù),官方提供了最佳實踐方案:

后臺任務(wù)處理最佳方案.png

WorkManager不熟悉,別的都很好理解,對于WorkManager 說明一下:
對于可延遲的、異步的、即使您的設(shè)備或應(yīng)用程序重新啟動也要運行的工作,請使用 WorkManager。WorkManager 可以在滿足工作條件(如網(wǎng)絡(luò)可用性和功率)時優(yōu)雅地運行可延遲的后臺工作。

  • WorkManager可以向后兼容到API 14+。
    • API 23+ 及以上版本實際上使用的是 JobScheduler
    • API 14-22 使用的是 AlarmManager& BroadcastReceiver
  • 可以添加任務(wù)執(zhí)行的約束條件,如網(wǎng)絡(luò)狀態(tài)、充電狀態(tài)
  • 可以處理一次性的異步任務(wù)、周期性的任務(wù)
  • 可以監(jiān)視,管理已經(jīng)安排好的任務(wù)
  • 任務(wù)執(zhí)行是有先后順序的,先觸發(fā)的先執(zhí)行
  • 能確保任務(wù)得到執(zhí)行,即使應(yīng)用重啟或設(shè)備重啟
  • 是遵從省電特性的,符合省電優(yōu)化要求

使用WorkManager的典型場景:

  • 發(fā)送logs、analytics 到后臺服務(wù)器
  • 周期性的從服務(wù)器獲取或同步數(shù)據(jù)

附:參考

官方文檔
WorkManagerSample

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容