日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

使用 WorkManager 管理后台和前台工作

發布時間:2024/4/15 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用 WorkManager 管理后台和前台工作 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

??
隨著設備性能提升和軟件生態發展,越來越多的 Android 應用需要執行相對更復雜的網絡、異步和離線等任務。例如用戶想要離線觀看某個視頻,又不想一直停留在應用界面等待下載完成,那么就需要以一定的方式讓這些離線的過程在后臺運行。再比如您想將一段精彩的 Vlog 分享到社交媒體,肯定也會希望視頻上傳時不會影響到自己繼續使用設備。這就涉及到了我們今天分享的主題: 使用 WorkManager 管理后臺和前臺工作。

如果您更喜歡通過視頻了解此內容,請在此處查看:

現代 WorkManager API 已發布

Bilibili 視頻鏈接
https://www.bilibili.com/video/BV16S4y1r7o9/

本文將著重探討 WorkManager 的 API 以及用法,幫助您深入了解它的運行機制,以及在實際開發中的使用方式。近期也將會有另一篇關于在 Android Studio 中如何更好地使用 WorkManager 的文章,敬請關注。

從首個穩定版本發布以來,WorkManager 提供了一些基礎 API,幫助您定義工作、放入隊列、依次執行,且在工作完成時通知您的應用。以功能劃分分類,這些基礎 API 包括:

最初的版本中,這些工作只能被定義為延遲執行,也就是它們會在定義之后延期再開始執行。通過這種延期執行策略,一些不緊急或優先級不高的任務將會推后執行。

WorkManager 的延期執行會充分考慮設備的低電耗狀態,以及應用的待機存儲分區,因此您不必考慮工作需要在哪個具體時間被執行,這些都交給 WorkManager 考慮即可。

WorkManager 支持對給定工作運行設定約束條件,約束可確保將工作延遲到滿足最佳條件時運行。例如,僅在設備采用不按流量計費的網絡連接時、當設備處于空閑狀態或者有足夠的電量時運行。您可以專心開發應用的其他功能,將對工作條件的檢查交給 WorkManager。

  • 約束

    https://developer.android.google.cn/topic/libraries/architecture/workmanager/how-to/define-work#work-constraints

我們知道,工作之間是可能存在依賴關系的。比如您正在開發一個視頻編輯應用,當剪輯完成后用戶可能需要分享到社交媒體,于是您的應用需要依次渲染若干個視頻片段,然后將它們一起上傳到視頻服務。這個過程是具有先后次序的,也就是上傳工作依賴渲染工作的完成。

再舉另外一個例子,當您的應用完成與后端同步數據后,也許您希望同步過程中產生的本地日志文件被及時清理,或者是將來自后端的新數據填充到本地數據庫中。于是您可以請求 WorkManager 按照順序或者并行執行這些工作,從而實現各個工作之間無縫銜接。而 WorkManager 會在確保所有給定條件都滿足后再運行后續的 Worker。

https://developer.android.google.cn/reference/androidx/work/Worker


很多具備與服務器同步功能的應用都具有這樣的特點: 應用與后端服務器的同步往往不是一次性的,它可能是需要多次執行的。比如當您的應用提供在線編輯服務時,一定需要頻繁將本地的編輯數據同步到云端,這就產生了定期執行的工作。

由于您可以隨時檢查某個工作的狀態,因此對于定期執行的工作而言,整個生命周期是透明的。您可以知道一個工作是處于隊列等待、運行中、阻塞還是已完成狀態。

上述的基礎 API 早在我們發布 WorkManager 的第一個穩定版時就已經提供了。首次在 Android 開發者峰會中談到 WorkManager 時,我們把它看作是管理可延期后臺工作的一個庫。如今從底層的角度來看,這種觀點仍然是成立的。但后來我們又添加了更多新功能,并讓 API 更符合現代規范。

這些新引入的 API 和改進的工具在為開發者提供更大便利的同時,也促使我們重新思考使用 WorkManager 的最佳時機。雖然從技術角度,我們設計 WorkManager 的核心思想仍然是正確的,但對于日益復雜的開發生態而言,WorkManager 的能力已經大大超過我們的設計預期。

工作的 “持久化” 特性

WorkManager 可以處理您指派給它的任何類型的工作,因此它已經進化成了一個專門處理任務且值得信賴的好工具。WorkManager 在全局作用域中執行您定義的 Worker,這意味著只要您的應用還在運行,不論是設備方向的變化,還是 Activity 被回收等,您的工作會被一直留存。不過單憑這一點,還不能稱之擁有 “持久化” 特性,因此 WorkManager 在底層還使用了 Room 數據庫來保證當進程被結束或設備重啟后,您的工作仍然可以執行,并有可能從中斷位置繼續執行。

執行需要長時間運行的工作

WorkManager 2.3 版本引入了對長時間運行的工作的支持。當我們談到長時間運行的工作時,指的是運行時間超過 10 分鐘執行窗口期的工作。通常情況下,一個 Worker 的執行窗口期被限定為 10 分鐘。為了能實現長時間運行的工作,WorkManager 將 Worker 的生命周期與前臺服務的生命周期捆綁在一起。JobScheduler 和進程內調度程序 (In-Process Scheduler) 仍然能感知到這種工作的存在。

https://developer.android.google.cn/reference/android/app/job/JobScheduler

由于前臺服務掌握著工作執行的生命周期,而前臺服務又需要向用戶展示通知信息,所以我們向 WorkManager 添加了相關的 API。用戶的注意力持續時間是有限的,所以 WorkManager 提供了 API 讓用戶能方便地通過通知停止長時間運行的工作。我們來分析一個長時間運行工作示例,代碼如下:

class DownloadWorker(context: Context, parameters: WorkerParameters) : CoroutineWorker(context, parameters) {fun notification(progress: String): Notification = TODO()// notification 方法根據進度信息生成一條 Android 通知消息。suspend fun download(inputUrl: String,outputFile: String,callback: suspend (progress: String) -> Unit) = TODO()// 定義一個用于分塊下載的方法fun createForegroundInfo(progress: String): ForegroundInfo {return ForegroundInfo(id, notification(progress))}override suspend fun doWork(): Result {download(inputUrl, outputFile) { progress -> val progress = "Progress $progress %"setForeground(createForegroundInfo(progress))} // 提供了一個 suspend 標記的 doWork 方法,其中調用下載方法,并顯示最新進度信息。return Result.success() } //下載完成后,Worker 只需要返回成功即可 }

這里有一個 DownloadWorker 類,它擴展自 CoroutineWorker 類。我們會在這個類當中定義一些輔助方法來簡化我們的工作。首先是一個 notification 方法,它可以根據所給定的進度信息生成一條 Android 通知消息。接下來我們要定義一個用于分塊下載的方法,這個方法接受三個參數: 下載文件的 URL、文件保存的本地位置、suspend 回調函數。每當某個分塊下載狀態變化時,此回調就會被執行一次。于是,回調中攜帶的信息就可以被用來生成一條通知。

CoroutineWorker
https://developer.android.google.cn/reference/kotlin/androidx/work/CoroutineWorker.html

有了這些輔助方法,我們就可以將 WorkManager 執行長時間運行工作所需要的 ForegroundInfo 實例保存起來。ForegroundInfo 是由通知 ID 和通知實例組合構造而成的,請繼續參照上述 CoroutineWorker 類的代碼示例。

在這段代碼里,我們提供了一個 suspend 標記的 doWork 方法,其中調用了剛才提到的分塊下載輔助方法。由于每次回調發生時都會提供一些最新的進度信息,所以我們可以利用這些信息來構建通知,并調用 setForeground 方法來向用戶顯示這些通知。這里調用 setForeground 的操作正是導致 Worker 長時間運行的原因。下載完成后,Worker 只需要返回成功即可,隨后 WorkManager 會將 Worker 的執行與前臺服務解耦分離、清理通知消息,并在必要時結束相關的服務。因此我們的 Worker 本身并不需要執行服務管理工作。

終止已提交執行的工作

用戶可能會突然改變主意,比如想要取消某個工作。某個前臺運行服務的通知是無法簡單滑動取消的,此前的做法是為這條通知消息添加一個動作,當用戶點擊時會向 WorkManager 發送一個信號,從而按照用戶的意圖終止某項工作。您也可以通過執行加急工作來終止,詳見后文。

fun notification(progress: String): Notification {val intent = WorkManager.getInstance(context).createCancelPendingIntent(getId())return NotificationCompat.Builder(applicationContext, id).setContentTitle(title).setContentText(progress)// 其他一些操作.addAction(android.R.drawable.ic_delete, cancel, intent).build() }

首先需要創建一個待處理的 Intent,它可以很方便地取消某項工作。我們需要調用 getId 方法來獲取這個工作創建時的工作請求 ID,然后調用 createCancelPendingIntent API 創建這個 Intent 實例。當此 Intent 被觸發時,它會向 WorkManager 發送取消工作的信號,從而實現取消工作的目的。

接下來就要生成帶有自定義動作的通知消息了。我們使用 NotificationCompat.Builder 設置通知的標題,然后添加一些文字說明。隨后調用 addAction 方法將通知中的 “取消” 按鈕與上一步創建的 Intent 關聯起來。于是,當用戶點擊 “取消” 按鈕時,這個 Intent 就會被發送到當前正在執行這個 Worker 的前臺服務,從而將其終止。

執行加急工作

Android 12 中引入了新的前臺服務限制,當應用在后臺時是無法啟動前臺服務的。因此從 Android 12 開始,調用 setForegroundAsync 方法會拋出 Foreground Service Start Not Allowed Exception (不允許啟動前臺服務) 異常。這種情況下,WorkManager 就派上用場了。WorkManager 2.7 版本中增加了對加急工作 (expedited work) 的支持,所以接下來將會向您介紹如何使用 WorkManager 實現終止已提交執行的工作。

https://developer.android.google.cn/jetpack/androidx/releases/work#2.7.0

從用戶的角度來說,加急工作是由用戶發起的,因此對用戶而言非常重要。甚至應用不在前臺時,這些工作也需要被啟動執行。比如聊天應用需要下載一條消息中的附件,或者應用需要處理付款訂閱的流程。在早于 Android 12 的 API 版本中,加急工作都是由前臺服務執行的,而從 Android 12 開始,它們將由加急作業 (expedited job) 實現。

系統以配額的形式限制了加急工作的數量。當應用處于前臺時,加急工作不存在任何配額限制,但是當應用轉到后臺運行時,就必須遵從這些限制。配額的大小取決于應用的待機存儲分區和進程重要性 (如優先級)。從字面意思來看,加急工作就是需要盡快啟動執行的工作,這意味著此類工作對于延遲相當敏感,所以也就不支持設定初始延遲或是定期執行的設置。由于受到配額限制,加急工作也不可以取代長時間運行的工作。當您的用戶想要發送一條重要信息時,WorkManager 會盡可能保證這條消息盡快發送。

class SendMessageWorker(context: Context, parameters: WorkerParameters): CoroutineWorker(context, parameters) {override suspend fun getForegroundInfo(): ForegroundInfo {TODO()}override suspend fun doWork(): Result {TODO()} }

例如,一個同步聊天應用消息的案例使用了加急工作 API。SendMessageWorker 類擴展自 CoroutineWorker,而它的作用是負責從后臺為聊天應用同步消息。加急工作需要在某個前臺服務的上下文中運行,這很類似于 Android 12 之前版本中的長時間運行的工作。因此我們的 Worker 類還需要實現 getForegroundInfo 接口,方便生成和顯示通知消息。但是在 Android 12 上 WorkManager 不會顯示其他的通知,這是因為我們定義的 Worker 背后是由加急作業實現的。您需要像平常那樣實現一個 suspend 標記的 doWork 方法。需要注意的是,當您的應用占用了全部的配額后,加急作業可能會被中斷。因此我們的 Worker 最好能跟蹤某些狀態,以便在重新安排執行時間后能夠恢復運行。

val request = OneTimeWorkRequestBuilder<ForegroundWorker>().setExpedited(OutOfQuotaPolicy.DROP_WORK_REQUEST).build()WorkManager.getInstance(context).enqueue(request)

您可以使用 setExpedited API 來安排加急工作,這個 API 會告訴 WorkManager,用戶認為給定的工作請求非常重要。由于所能安排的工作存在配額限制,所以您需要表明當應用的配額用盡時該怎么處理,有兩種備選方案: 其一是將加急請求變成常規工作請求,其二是在配額耗盡時放棄新的工作請求。

從 2.5 版本開始,WorkManager 對支持多進程的應用進行了若干項改進。如果您需要使用多進程 API,就需要定義 work-multiprocess 工件的依賴項,多進程 API 的目標是在輔助進程中對 WorkManager 的冗余部分或高開銷部分進行大范圍初始化操作。比如有多個進程在同時獲取統一底層 SQLite 數據庫的事務鎖,這時就會發生 SQLite 爭用;而這種爭用正是我們想要通過多進程 API 減少的。另一方面,我們還想確保進程內調度程序在正確的進程中運行。

為了解 WorkManager 初始化時哪些部分是冗余的,我們需要清楚它會在后臺執行哪些操作。

單進程的初始化


首先觀察一下單進程初始化過程。應用啟動后,第一件事是有平臺調用 Application.onCreate 方法。隨后在進程生命周期的某個時間點,WorkManager.getInstance 會被調用以啟動 WorkManager 的初始化。當 WorkManager 初始化完畢后,我們運行 ForceStopRunnable。這個過程很重要,因為此時 WorkManager 會檢查應用之前是否被強制停止過,它會比較 WorkManager 存儲的信息與 JobScheduler 或 AlarmManager 中的信息,確保作業都被準確編入執行計劃中。同時,我們也可以重新安排此前中斷的某些工作,比如進程崩潰后進行的一些恢復工作。大家都知道,這樣做的開銷非常高,我們需要在多個子系統中比較和協調狀態,但是理想狀態下,這種操作只應該被執行一次。另外需要注意,進程內調度程序只在默認進程中運行。

  • JobScheduler

    https://developer.android.google.cn/reference/android/app/job/JobScheduler.html

  • AlarmManager

    https://developer.android.google.cn/reference/android/app/AlarmManager

多進程的初始化


接著我們再看看如果應用有第二個進程會發生什么。假如應用有第二個進程,基本上它會重復在第一個進程中完成的各項操作。首先第一個進程如上文那樣初始化,并且由于這是主進程 (primary process),所以進程內調度程序 (In-Process Scheduler) 也會在其中運行。對于第二個進程,我們會重復剛才的過程,再次調用 Application.onCreate,然后重新初始化 WorkManager。這意味著,我們將重復在第一個進程中所做的所有工作。

根據前面的分析,您也許會感到疑惑,為什么還需要再次執行 ForceStopRunable 呢?這是由于 WorkManager 并不知道這些進程中哪一個優先級較高。如果應用是屏幕鍵盤或者微件 (Widget),那么主進程可能并不等同于默認進程。另外,輔助進程 (secondary processes) 中也沒有運行進程內調度程序 (因為它不是默認進程)。其實進程內調度程序所在的進程選擇非常重要,由于它不受其他持久性調度器的限制影響,所以調整其所在的進程可以顯著提升數據吞吐量。例如,JobScheduler 的作業上限是 100 個,而進程內調度程序則沒有這個限制。

val config = Configuration.Builder().setDefaultProcessName("com.example.app").build()

通過 WorkManager 定義主進程

我們來看看如何定義指定的默認進程。首先根據自己的意愿設置默認進程的名稱,這通常是應用的軟件包名稱,一旦定義了應用的默認進程,那么進程內調度程序就會在其中運行。但是輔助進程怎么辦?有沒有辦法能夠防止在其中再次初始化 WorkManager?事實證明這是可以辦到的。其實我們真正需要的是完全不必初始化 WorkManager。

為了實現這個目標,我們引入了 RemoteWorkManager。這個類需要綁定到指定進程 (主進程),并使用綁定服務將次要進程的所有工作請求轉發到這個指定的主進程。這樣一來,您就可以完全避免所有剛才提到的跨進程 SQLite 爭用,因為從開始到結束只有唯一一個進程在向底層 SQLite 數據庫寫入數據。您可以跟往常一樣在輔助進程中創建工作請求,但是此處應該使用 RemoteWorkManager 而不是 WorkManager。使用 RemoteWorkManager 后,會通過綁定服務綁定到主進程中,并將所有工作請求進行轉發,然后存儲到特定隊列等待執行。您可以通過將 RemoteWorkManager 服務合并到應用的 Android Manifest RXML 中實現這個綁定。

val request = OneTimeWorkRequestBuilder<DownloadWorker>().build()RemoteWorkManager.getInstance(context).enqueue(request) <!-- AndroidManifest.xml --> <serviceandroid:name="androidx.work.multiprocess.RemoteWorkManagerService"android:exported="false" />

不同進程中運行 Worker

我們已經了解如何通過 WorkManager 定義主進程來避免爭用,但有時候,您也希望能夠在不同的進程中運行 Worker。舉個例子,如果您在某應用的輔助進程中運行機器學習工作流 (ML Pipeline),而且該應用還有專門的界面進程,那么您可能需要在不同的進程中運行不同的 Worker。比如在輔助進程中隔離執行某個工作,這樣一來即使這個進程內出現錯誤而崩潰也不會導致應用的其他部分癱瘓而整體退出,尤其是要保障界面進程正常工作。要實現在不同進程中執行 Worker,您需要擴展 RemoteCoroutineWorker 類。這個類與 CoroutineWorker 類似,擴展之后您需要自己實現 doRemoteWork 接口。

public class IndexingWorker(context: Context,parameters: WorkerParameters ): RemoteCoroutineWorker(context, parameters) {override suspend fun doRemoteWork(): Result {doSomething()return Result.success()} }

由于這個方法是在輔助進程中執行的,我們仍然要定義 Worker 需要與哪個進程綁定。為此,我們還需要在 Android Manifest RXML 中添加一個條目。一個應用可以定義多項 RemoteWorker 服務,每項服務都在獨立的進程中運行。

<!-- AndroidManifest.xml --> <serviceandroid:name="androidx.work.multiprocess.RemoteWorkerService"android:exported="false"android:process=":background" />

這里您可以看到,我們為名為 background 的輔助進程添加了新服務。現在,您已經在 RXML 中定義好了服務,還需要進一步在工作請求中指明要綁定的組件名稱。

al inputData = workDataOf(ARGUMENT_PACKAGE_NAME to context.packageName,ARGUMENT_CLASS_NAME to RemoteWorkerService::class.java.name )val request = OneTimeWorkRequestBuilder<RemoteDownloadWorker>().setInputData(inputData).build()WorkManager.getInstance(context).enqueue(request)

組件名稱是軟件包名和類名的組合,您需要將其添加到工作請求的輸入數據中,然后用這個輸入數據創建工作請求,這樣一來 WorkManager 就知道要綁定哪項服務了。我們照常將工作放入隊列中,當 WorkManager 準備執行這項工作時,它首先根據輸入數據中定義的內容找到綁定的服務,并執行 doRemoteWork 方法。這樣一來,所有復雜繁瑣的跨進程通信的任務都交給 WorkManager 來處理了。


WorkManager 是應對長執行時間工作的推薦方案,推薦您使用 WorkManager 實現請求和取消長時間運行的工作任務。通過本文了解到如何以及何時使用加急工作 API,如何編寫可靠的高性能多進程應用。


希望這篇文章對您有所幫助!

如需更多資源,請參閱:

  • Codelab: 使用 WorkManager 處理后臺任務
    https://developer.android.google.cn/codelabs/android-workmanager

  • Codelab: WorkManager 進階知識
    https://developer.android.google.cn/codelabs/android-adv-workmanager

  • WorkManager 示例代碼
    https://github.com/android/architecture-components-samples/tree/main/WorkManagerSample

總結

以上是生活随笔為你收集整理的使用 WorkManager 管理后台和前台工作的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。