Android後台任務開發的發展和實踐(一)
來自專欄 Android Pub3 人贊了文章
這篇文章主要大致講解在android上實現後台任務的幾種方法,它們之間的區別以及開發中的最佳實踐。
後台任務 (background tasks/operations) 是一個很寬泛的概念,在android上我們通常指非主線程的任務,例如網路操作,讀寫資料庫/文件或是定時任務等等,這些任務通常耗時較久因而需要非同步處理。Android給開發者提供了很多不同的api來做這件事情,比如AsyncTask, Loader, IntentService, JobIntentService, AlarmManager, JobDispatcher, JobScheduler, WorkManager... ah,除了以上這些,我們還有rxjava (the list might keep growing)
AsyncTask出現在API 3中,相信大家在android開發的初期就已經接觸過了。一個有趣的事情是AsyncTask最初設計是在一個後台線程上串列執行所有的任務,在API 4中為了提升效率變成了多線程並行處理,2年之後在android 3.0 (API 11) 因為有太多應用並發錯誤中又改回了默認單線程串列處理 :shrug
AsyncTask的出現是為了簡化android上後台線程處理以及和主線程的通信,但是有一個問題是它並不能很好的處理 activity/fragment 的生命周期。一個我們經常遇到的問題就是用戶旋轉屏幕之後,當前activity被重新創建,但是AsyncTask會繼續運行並持有前一個activity的reference,這樣不僅導致前一個activity沒有辦法被立刻銷毀,而且onPostExecute
對UI的更新會沒有效果因為其嘗試更新的是前一個activity。
所以我們經常需要對AsyncTask做額外的生命周期管理,比如在activity#onStop
中去取消所有還沒有結束的AsyncTask,(當然你可以通過阻止activity/fragment的新建去解決這個問題, but its really a terrible idea, think twice before doing anything like that)。隨之而來的另一個坑就是AsyncTask#cancel
is best effort, 而且cancel並不會立刻 阻斷doInBackground
的任務執行, 所以我們需要不斷檢查isCancelled
確保任務可以儘快取消掉。如果你想要了解的更多AsyncTask的問題,Dan Lew 的這篇文章有更深入的講解。
AsyncTask的這些缺點確實給我們帶來了很多boilerplate code而且一不小心就容易出錯,我一般只有在非同步操作滿足以下條件的時候會使用AsyncTask
- Simple and quick operations - to avoid blocking other possible async tasks as its running in serial by default
- No UI update involved - to avoid extra lifecycle management
- Killable - the task can be killed/cancelled if necessary
如果是滿足上述三個條件的操作的話,我們可以直接使用AsyncTask.execute(Runnable r)
就可以了,e.g. 非同步獲取更新設備的ad info AdvertisingIdClient.getAdvertisingIdInfo
.
其實Loader的出現(API 11)本來就是為了解決上面提到的AsyncTask lifecycle問題,AsyncTaskLoader
是Loader的一個實現,從名字中也可以看出設計的初衷。但是也有很多缺陷一直被開發者抱怨,confusing api, testability and boilerplate code, etc. 在API 28中,Loader已被官方deprecated,推薦使用 ViewModel
+ LiveData
.
Loaders have been deprecated as of Android P (API 28). The recommended option for dealing with loading data while handling the Activity and Fragment lifecycles is to use a combination of
ViewModels
andLiveData
. ViewModels survive configuration changes like Loaders but with less boilerplate. LiveData provides a lifecycle-aware way of loading data that you can reuse in multiple ViewModels.
If youve never used loader before, congrats, time to go with architecture component directly!
Service在後台任務處理中又扮演了怎樣的一個角色呢?Service設計之初就是為了可以在後台執行long-running operations, 一個常見的誤解就是Service就是運行在background thread上的,我想其中一大原因就是來自Service的定義,background
+ long-running operations
很容易給人有錯誤的假定。
A
Service
is an application component that can perform long-running operations in the background, and it doesnt provide a user interface.
Service默認運行在當前應用進程的主線中,所以如果需要執行blocking or intensive work (different from long-running operations),我們還是需要自己創建新的進程。為了方便開發,Android實現了IntentService
,它通過一個後台線程去處理所有的任務請求,需要注意的是任務的處理是串列的。
那麼IntentService
或是通過Service實現的其它後台任務處理和AsyncTask
或是Thread
的區別是什麼呢?
雖然它們本質上都是對Thread的封裝,但是它們有完全不同的設計初衷。對於AsyncTask而言,更多的和Activity/UI關聯,而且任務處理通常是要求很短,而Service更多的是在應用處於後台(沒有用戶交互或是UI更新)時發揮作用,而且在任務沒有完成之前,Service可以要求在系統資源充足時重啟。
JobIntentService
又是怎麼一回事? 這些緣起於Android對後台任務的限制和優化。我們之後在後台一直運行Service其實很吃系統資源,特別是有不少無良App會通過Service常駐後台,給用戶帶來了很差的用戶體驗。所以Android N(Android 7.0)和 Android O(Android 8.0)系統對Background Service
和Broadcast
做了一定的限制,其中的一些限制包括:
- 不可以在應用處於後台狀態時call
startService
啟動Service,IllegalStateException
will be thrown otherwise - 當應用退到後台之後,系統會給應用幾分鐘左右的時間繼續運行之前所創建啟動的後台服務,之後便會被系統標記為空閑狀態,並停止該後台服務。
Above limitations are only background service as foreground service is more visible to users.
對於Broadcast的限制包括:
- Android 7.0 中,app can NOT send or receive ACTION_NEW_PICTURE or ACTION_NEW_VIDEO broadcast
- App targeting Android 7.0 and higher cant receive CONNECTIVITY_ACTION if its declared in manifest
- From Android O,all implicit broadcast receivers declared in manifest file will not work
這麼做的原因很簡單,因為implicit broadcast是一個很浪費系統資源的事情。比如有10個應用註冊並監聽了這個CONNECTIVITY_ACTION這個廣播,當用戶手機的網路連接發生變化時,這10個應用都會被喚起去處理這個事件,但可能其中90%的應用只關心用戶是否連接到wifi以便同步數據,那麼當用戶失去網路連接或是連接到metered network時,這些應用都被不必要的喚醒了。
為了解決這些問題,並給開發者提供更加智能的後台任務處理,Android提出了Job和JobScheduler的概念,在下一篇文章中我們將會有進一步的了解。
謝闖:Android後台任務開發的發展和實踐(二)推薦閱讀: