Android 后臺(tái)任務(wù)Services、WorkManager總結(jié)

Android O對(duì)應(yīng)用在后臺(tái)運(yùn)行時(shí)可以執(zhí)行的操作施加了限制,稱為后臺(tái)執(zhí)行限制(Background Execution Limits),這可以大大減少應(yīng)用的內(nèi)存使用和耗電量,提高用戶體驗(yàn)。后臺(tái)執(zhí)行限制分為兩個(gè)部分:后臺(tái)服務(wù)限制(Background Service Limitations)、 廣播限制(BroadcastLimitations)。

后臺(tái)服務(wù)限制

除了下面情況外都是后臺(tái)應(yīng)用:

  1. 具有可見(jiàn)的Activity
  2. 具有前臺(tái)服務(wù)
  3. 另一個(gè)前臺(tái)應(yīng)用已關(guān)聯(lián)到該應(yīng)用(通過(guò)bindService或者使用該應(yīng)用的ContentProvider)。

應(yīng)用在后臺(tái)期間保留其后臺(tái)服務(wù)的能力將受到限制。如果應(yīng)用處于后臺(tái)時(shí)調(diào)用了startService()將會(huì)拋出IllegalStateException,除非:

  • 應(yīng)用已經(jīng)處于前臺(tái),則可以調(diào)用 startService(),不會(huì)拋出IllegalStateException,但一旦進(jìn)入后臺(tái),后臺(tái)應(yīng)用將被置于一個(gè)臨時(shí)白名單中,位于白名單中時(shí),在這段時(shí)間內(nèi),應(yīng)用可以無(wú)限制地啟動(dòng)服務(wù),其后臺(tái)服務(wù)也可以運(yùn)行。但這個(gè)時(shí)間期一過(guò)(Nexus 5X 8.0 系統(tǒng)上測(cè)試不到1分鐘),應(yīng)用就會(huì)進(jìn)入空閑狀態(tài),后臺(tái)服務(wù)就會(huì)被銷毀。

因此,通過(guò)startservices啟動(dòng)的服務(wù)有如下特點(diǎn):

  1. 在后臺(tái)運(yùn)行的服務(wù)在幾分鐘內(nèi)會(huì)被stop掉(模擬器測(cè)試在1分鐘左右后被kill掉)。在這段時(shí)間內(nèi),應(yīng)用仍可以創(chuàng)建和使用服務(wù)。
  2. 在應(yīng)用處于后臺(tái)幾分鐘后(模擬器測(cè)試1分鐘左右),應(yīng)用將不能再通過(guò)startService創(chuàng)建后臺(tái)服務(wù),如果創(chuàng)建則拋出以下異常
Caused by: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.example.xxxx.test/.TestService }: app is in background

也就是說(shuō),當(dāng)你的應(yīng)用不在前臺(tái),時(shí)間窗結(jié)束后,會(huì)變成閑置狀態(tài),系統(tǒng)就殺死你的后臺(tái)服務(wù)。網(wǎng)上一系列Service?;?,創(chuàng)建永不停止Service,經(jīng)過(guò)驗(yàn)證,都已失效。即你無(wú)法在使用后臺(tái)服務(wù)在后臺(tái)偷偷執(zhí)行需要長(zhǎng)時(shí)間的任務(wù),例如監(jiān)控GPS狀態(tài),記錄步數(shù)等等。限制后臺(tái)服務(wù)使用的原因是,當(dāng)你的app使用服務(wù)在后臺(tái)運(yùn)行時(shí),你的app消耗了寶貴的資源:- 內(nèi)存 - 電池。最佳的做法是:操作完成后,服務(wù)應(yīng)自行停止。許多應(yīng)用程序具有長(zhǎng)時(shí)間運(yùn)行的后臺(tái)服務(wù),這些服務(wù)基本上運(yùn)行無(wú)限時(shí)間以維持與服務(wù)器的Socket連接或監(jiān)視某些任務(wù)或用戶活動(dòng)。這些服務(wù)會(huì)導(dǎo)致電池耗盡,并且還會(huì)不斷消耗內(nèi)存。鑒于以上原因:后臺(tái)服務(wù)已經(jīng)無(wú)法再在后臺(tái)執(zhí)行長(zhǎng)時(shí)間任務(wù)。安卓推出了以下方案來(lái)解決此類問(wèn)題:

  • 前臺(tái)服務(wù)Android 8.0 引入了一種全新的方法,即Context.startForegroundService(),以在前臺(tái)啟動(dòng)新服務(wù)。在系統(tǒng)創(chuàng)建服務(wù)后,應(yīng)用有五秒的時(shí)間來(lái)調(diào)用該服務(wù)的startForeground() 方法以顯示新服務(wù)的用戶可見(jiàn)通知。如果應(yīng)用在此時(shí)間限制內(nèi)未調(diào)用
    startForeground(),則系統(tǒng)將停止服務(wù)并聲明此應(yīng)用為 ANR。

  • WorkManager:WorkManager適用于那些即使應(yīng)用程序退出,系統(tǒng)也能夠保證這個(gè)任務(wù)正常運(yùn)行的場(chǎng)景,比如將應(yīng)用程序數(shù)據(jù)上傳到服務(wù)器。它不適用于應(yīng)用進(jìn)程內(nèi)的后臺(tái)工作,如果應(yīng)用進(jìn)程消失,就可以安全地終止。

  • JobScheduler: JobScheduler 作業(yè)替換后臺(tái)服務(wù)。 例如,CoolPhotoApp需要檢查用戶是否已經(jīng)從朋友那里收到共享的照片,即使該應(yīng)用未在前臺(tái)運(yùn)行。

前臺(tái)服務(wù)

對(duì)于需要立即運(yùn)行并且必須執(zhí)行完畢的由用戶發(fā)起的工作,請(qǐng)使用前臺(tái)服務(wù)。使用前臺(tái)服務(wù)可告知系統(tǒng)應(yīng)用正在執(zhí)行重要任務(wù),不應(yīng)被終止。前臺(tái)服務(wù)通過(guò)通知欄中的不可關(guān)閉通知向用戶顯示。

  1. 修改啟動(dòng)方式
Intent service = new Intent(this, MyBackgroundService.class);
service.putExtra("startType", 1);
if (Build.VERSION.SDK_INT >= 26) {
    startForegroundService(service);
} else {
    startService(service);
}
  1. 啟動(dòng)完前臺(tái)service, 一定記得在5s以內(nèi)要執(zhí)行startForeground方法,不然就會(huì)出現(xiàn)ANR。
public class MyBackgroundServiceextends Service {

    @Override
    public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //適配安卓8.0
            String channelId = getChannelId() + "";
            String channelName = getChannelName();
            NotificationChannel channel = new NotificationChannel(channelId, channelName,
                    NotificationManager.IMPORTANCE_MIN);
            NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            manager.createNotificationChannel(channel);
            startForeground(getChannelId(), getNotification());
        }
    }
    ...
}

一樣還是先判斷系統(tǒng)版本, 如果高于26就調(diào)用startForeground方法好了, 使用startForegroundService 方法啟動(dòng)后臺(tái)service這么使用即可。不過(guò)這樣會(huì)在通知欄里面彈出彈出通知。

WorkManager

WorkManager適用于那些即使應(yīng)用程序退出,系統(tǒng)也能夠保證這個(gè)任務(wù)正常運(yùn)行的場(chǎng)景,比如將應(yīng)用程序數(shù)據(jù)上傳到服務(wù)器。它不適用于應(yīng)用進(jìn)程內(nèi)的后臺(tái)工作,如果應(yīng)用進(jìn)程消失,就可以安全地終止

WorkManager 不適用于需要在特定時(shí)間觸發(fā)的任務(wù),也不適用立即任務(wù)。針對(duì)特定時(shí)間觸發(fā)的任務(wù)使用 AlarmManager,立即執(zhí)行的任務(wù)使用 ForegroundService。

WorkManager適用于那些在應(yīng)用退出之后任務(wù)還需要繼續(xù)執(zhí)行的需求(比如應(yīng)用數(shù)據(jù)上報(bào)服務(wù)器的情況),對(duì)應(yīng)那些在應(yīng)用退出的之后任務(wù)也需要終止的情況就需要選擇ThreadPool、AsyncTask來(lái)實(shí)現(xiàn)。

對(duì)于可延遲的工作以及預(yù)計(jì)即使您的設(shè)備或應(yīng)用重啟也會(huì)運(yùn)行的工作,請(qǐng)使用 WorkManager。WorkManager 是一個(gè) Android 庫(kù),可在滿足工作的條件(例如網(wǎng)絡(luò)可用性和電源)時(shí)妥善運(yùn)行可延遲的后臺(tái)工作。

WorkManager 提供向后兼容的 API(兼容 API 級(jí)別 14 及更高級(jí)別),利用 JobScheduler API(API 級(jí)別 23 及更高級(jí)別)幫助優(yōu)化更低級(jí)別設(shè)備上的電池續(xù)航時(shí)間、批量作業(yè)以及 AlarmManagerBroadcastReceiver 的組合。

WorkManager是一個(gè)用于排隊(duì)可延遲工作的庫(kù),保證在滿足約束條件后的某個(gè)時(shí)間執(zhí)行。 WorkManager允許觀察工作狀態(tài)和創(chuàng)建復(fù)雜工作鏈的能力。
WorkManager旨在通過(guò)為系統(tǒng)驅(qū)動(dòng)的后臺(tái)處理提供一流的API來(lái)簡(jiǎn)化開(kāi)發(fā)人員體驗(yàn)。它適用于即使應(yīng)用程序不再位于前臺(tái)也應(yīng)運(yùn)行的后臺(tái)作業(yè)。在可能的情況下,它使用JobScheduler或Firebase JobDispatcher來(lái)完成工作; 如果你的應(yīng)用程序在前臺(tái),它甚至?xí)L試直接在你的過(guò)程中完成工作。
應(yīng)用場(chǎng)景:每15分鐘跟蹤用戶位置

WorkManager 和AsyncTask, ThreadPool, RxJava的區(qū)別:這三個(gè)和WorkManager并不是替代的關(guān)系. 這三個(gè)工具, 能幫助你在應(yīng)用中開(kāi)后臺(tái)線程干活, 但是應(yīng)用一被殺或被關(guān)閉, 這些工具就干不了活了. 而WorkManager不是, 它在應(yīng)用被殺, 甚至設(shè)備重啟后仍能保證你安排給他的任務(wù)能得到執(zhí)行.

定義 Worker

咱們定義 MainWorker 繼承 Worker,發(fā)現(xiàn)須要重寫(xiě) doWork 方法,而且須要返回任務(wù)的狀態(tài) WorkerResult:

class MainWorker : Worker() {
    override fun doWork(): WorkerResult {
        // 要執(zhí)行的任務(wù)
        return WorkerResult.SUCCESS
    }
}
定義 WorkRequest

在 MainActivity 中定義 WorkRequest:

val request = OneTimeWorkRequest.Builder(MainWorker::class.java).build()

OneTimeWorkRequest 意味著這個(gè)任務(wù)只需執(zhí)行一遍。

加入任務(wù)隊(duì)列

要讓任務(wù)執(zhí)行,須要將 WorkRequest 加入任務(wù)隊(duì)列:

WorkManager.getInstance().enqueue(request)

AlarmManager

如果您需要在確切的時(shí)間運(yùn)行某項(xiàng)作業(yè),請(qǐng)使用 AlarmManagerAlarmManager 會(huì)在您指定的時(shí)間啟動(dòng)應(yīng)用(如有必要),以便運(yùn)行該作業(yè)。但是,如果您的作業(yè)不需要在確切的時(shí)間運(yùn)行,則 WorkManager 是更好的選擇;WorkManager 能更好地平衡系統(tǒng)資源。例如,如果您需要大約每小時(shí)運(yùn)行一次某項(xiàng)作業(yè),但不需要在特定時(shí)間運(yùn)行該作業(yè),則應(yīng)使用 WorkManager 設(shè)置周期性作業(yè)。

DownloadManager

如果您的應(yīng)用執(zhí)行長(zhǎng)時(shí)間運(yùn)行的 HTTP 下載,請(qǐng)考慮使用 DownloadManager??蛻舳丝赡軙?huì)請(qǐng)求將 URI 下載到位于應(yīng)用進(jìn)程之外的特定目標(biāo)文件中。內(nèi)容下載管理器會(huì)在后臺(tái)執(zhí)行下載操作,它負(fù)責(zé)處理 HTTP 互動(dòng),在下載失敗或連接發(fā)生更改以及系統(tǒng)重新啟動(dòng)后重新嘗試下載。

后臺(tái)任務(wù)選擇

場(chǎng)景 推薦
需系統(tǒng)觸發(fā),不必完成 ThreadPool + Broadcast
需系統(tǒng)觸發(fā),必須完成,可推遲 WorkManager
需系統(tǒng)觸發(fā),必須完成,立即 ForegroundService + Broadcast
不需系統(tǒng)觸發(fā),不必完成 ThreadPool
不需系統(tǒng)觸發(fā),必須完成,可推遲 WorkManager
不需系統(tǒng)觸發(fā),必須完成,立即 ForegroundService

Android Jetpack - 使用 WorkManager 管理后臺(tái)任務(wù)
Android后臺(tái)Service已死 WorkManager崛起
Android P后臺(tái)服務(wù)被終止,創(chuàng)建永不終止的后臺(tái)服務(wù)
Android后臺(tái)任務(wù)
學(xué)習(xí)Android Jetpack? 實(shí)戰(zhàn)和教程這里全都有!
Android Jetpack - 使用 WorkManager 管理后臺(tái)任務(wù)

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

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