2020-08-27
1、什么是IntentService
android.app.IntentService 的本質就是一個 android.app.Service。
它需要在 AndroidManifest.xml 中注冊 <service /> 節點,並可以 startService() 的形式啟動。它是Android官方引入的用於執行“耗時任務”的服務抽象類。
傳統的 Service 里的代碼都是運行在主線程里的,而對於服務這種天生就被設計成更適合“在后台默默工作”的機制來說,我們又常常需要它執行一些耗時型的操作,主線程是不建議做耗時操作的。因此,我們常常會在服務中創建子線程來做耗時操作。而IntentService就是Android官方提供的在普通Service里搭建好了用於執行耗時任務的子線程的特殊Service。
2、IntentService的原理
IntentService的本質就是一個普通Service里面加裝了一套運行在子線程的Handler機制。
我們還是直接去看它的原碼實現吧,反正也很簡單。
IntentService是一個抽象類,它不多的幾個核心成員如下所示:
public abstract class IntentService extends Service { private volatile Looper mServiceLooper; private volatile ServiceHandler mServiceHandler; protected abstract void onHandleIntent(@Nullable Intent intent); }
一個Looper, 一個Handler以及一個用於處理具體事物的抽象方法。Looper和Handler是由IntentService負責並管理的,我們僅需要關心那個抽象方法即可。
下面來看看Looper與Handler的初始化流程。
IntentService 的 onCreate() 如下圖所示:
首先它創建了一個 HandlerThread 對象,這個 HandlerThread 也是Android官方提供的,它其實就是在一個子線程內部自己創建並管理了一個 Looper,HandlerThread的核心代碼如下圖所示:
根據上圖所示代碼,HandlerThread子線程在運行以后會直接卡在 Looper.loop() 方法處直至程序主動調用那個Looper的退出方法。它這么做其實就為了維護 mLooper 的對象不被GC回收掉。
我們接着看IntentService的onCreate()實現。
HandlerThread在子線程運行起來以后就去拿它剛創建出來的 mLooper 對象,然后再以這個 mLooper 對象構建一個 ServiceHandler對象。ServiceHandler是IntentService的內部類,它的實現也很簡單,如下圖所示:
就是一個普通的Handler,只不過它的Looper是一個由子線程創建並維護的罷了。並在接收到Message消息后傳遞給前面提到的那個抽象方法去處理。在消息處理完成以后立即主動結束IntentService服務。不過這里雖然在每次執行完一個請求后就立即調用停止服務的方法,但是它仍然會等到所有請求都執行完成后才會真正去 onDestroy()。
這里再額外強調一點:抽象方法onHandleIntent()是執行在子線程中的。
整個 IntentService 的原理就是這么簡單,正如前面所說,它其實就是普通的Service加裝了一套在子線程中執行請求的機制而已。
3、IntentService的通俗用法
IntentService不推薦使用“bindService()”的方式啟動,因此,它的啟動方法就只能是"startService()"了:
Intent it = new Intent(); it.setClass(MainActivity.this, MyIntentService.class); startService(it);
簡單到窒息。
4、IntentServicve的另類用法:全局顯示提示框
這一小節的內容適用於從事嵌入式產品系統開發的同學。
當我們在做系統開發的時候,常常會遇到需要彈出信息提示框的需求。如果只是普通的APK要彈倒也還好解決,但仍然有不少場景是framework層甚至是更底層service需要彈出提示消息的,這些就會稍微麻煩一點。
再退一步來說,即使能解決各種場景下的彈窗問題,從軟件設計角度來說,或者從系統設計角度來說這種零散而重復的代碼風格也是很糟糕的設計。
一個系統集成廠商往往需要一套更統一更抽象的解決辦法。
筆者很喜歡的一種做法就是創建一個APK,專用於彈出消息提示框。縱觀Android四大組件,最合適的莫過於Service了。
於是,在Service的onStartCommand()中彈出這個提示框就顯得既簡單又可靠了。
但這又會帶來另一個問題:Service也是有生命周期的,雖然其生命周期結束以后不見得會立即回收相關內存資源,前面彈出的提示框仍能繼續保持顯示,但這總歸是一個風險點。當系統的內存較為緊張時就有可能出現這個提示框“意外消失”的情況。這很不友好!
怎么辦?
既然問題是因為服務的生命周期結束了導致相關資源被GC回收而引發的,那我們就讓這個服務在提示框被正常關閉之前不要跑完生命周期就好了。
方案有了,如何實現呢?
答案就是IntentService。
前面我們知道IntentService是可以執行耗時操作的,且在那個運行在子線程的抽象方法執行完畢之前,服務是不會跑完它的生命周期的,那么我們完全可以在 onStartCommand() 中彈出對話框,然后在抽象方法中直接跑個死循環,監聽這個提示框的顯示狀態,當提示框正常關閉后再退出這個抽象方法中的死循環,讓IntentService跑完它的生命周期不就行了?
這么做確實可以,且與前面僅在Service中彈提示框的方式相比,它的 /proc/<pid>/oom_adj 的值要優秀上很多。這個值含義可以參考博客: https://www.jianshu.com/p/8897b7e47466
具體的代碼這里就不貼了,原理這么簡單實現起來同樣也非常簡單。