基本概念
IntentService作用
IntentService是Service類的子類,常用來在後臺執行耗時的異步請求。我們不用去關心Service的創建和銷燬的細節。也不用單獨開線程,只管處理自己的任務,處理完過後系統會自動銷燬該服務,啓動IntentService的方式和普通Service相同,但是使用起來卻極其簡單。使用示例如下:
//自定義一個IntentService
public class MyService extends IntentService {
public MyService() {
super("MyService");
}
@Override
protected void onHandleIntent(Intent intent) {
String url=intent.getStringExtra("url");
//在這處理你的任務
//...
}
}
//啓動IntentService
Intent intent= new Intent(this,MyService.class);
intent.putExtra("url","......");
startService(intent);
IntentService疑問
看完上面的基本概念後,你可能會產生如下疑問。
- IntentService構造方法傳的那個參數是來幹嘛的
- IntentService內部是怎麼工作的?
- IntentService怎麼做到任務結束後自動銷燬?
讓我們帶着疑問,再次去源碼裏探索吧。
初識API
HandlerThread
初看這個名字,心裏在想,難道是內部自帶Handler的線程?筆者懷着好奇的心,點開了HandlerThread這個類。因爲HandlerThread繼承於Thread,於是反射性的找到了run
方法裏面的源碼進行研究,源碼如下:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();//初始化Looper
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();//開啓Looper無限循環
mTid = -1;//這句話永遠執行不到,除非Looper被退出
}
果然如此,很常規的Looper
初始化。有Looper
,肯定會有Handler
,不然Looper
豈不是浪費了。但是在Looper.prepare();
和Looper.loop()
之間,並沒有看到Handler
的蹤影。筆者心想,莫非谷歌用了什麼黑科技用法,於是快速把源碼過了一遍。WTF!Handler
呢?並沒有找到啊。既然沒有,幹嘛叫HandlerThread
,這名字取的只給1分。於是查看了API說明。說明如下:
Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.
大意:這個類用於創建一個擁有Looper的線程。這個Looper可以用來創建Handler類,雖然如此,start()仍然必須調用。
看到這裏算是明白了,原來這個線程只是用來提供Looper的啊,以免我們在子線程中使用Handler過於麻煩。必須調用start是因爲,Looper的初始化在run
方法內。使用示例如下:
//啓動帶Looper的線程
HandlerThread handlerThread=new HandlerThread("handlerThread");
handlerThread.start();
//初始化Handler
Handler handler=new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
// 在這裏接收消息,這裏的處理是在子線程中執行的。
}
};
handler.sendMessage(..)//發送消息
筆者心想,由於線程的不穩定性,handlerThread.getLooper()
會不會取到個空值啊。點進去一看,瞬間覺得自己想多了。可以看到,如果爲空就將線程掛起等待,即時你手動喚醒,還有個while循環進行保障。
public Looper getLooper() {
//...
//省略部分源碼
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
當Looper初始化完畢,就會喚醒等待的線程,喚醒方法就在run
方法中。
public void run() {
//...
//省略部分源碼
Looper.prepare();//初始化Looper
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();//喚醒等待線程
}
//...
//省略部分源碼
Looper.loop();//開啓Looper無限循環
}
可以看出handlerThread.getLooper()
是個阻塞方法。
工作原理
IntentService的用法,前面已經介紹過了,現在開始來梳理流程。
我們知道,只需在onHandleIntent
裏面編寫耗時任務,系統就會自動開啓線程去處理,處理完畢後,自動銷燬Service。這一過程,尤其顯得格外人性化。
那麼,IntentService內部流程是怎麼樣的?IntentService繼承與Service,那我們就按照Service的生命週期來一步一步進行探索,由於源碼比較簡單,這裏就快速過一遍。
onCreate
@Override
public void onCreate() {
super.onCreate();
//啓動帶Looper的線程
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
//初始化Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
可以看出,用了HandlerThread
,同時也初始化了一個Handler
。看完這裏。我想你心裏一定有譜了。大約明白了內部工作原理。通過HandlerThread
,利用Handler
發送消息轉移到子線程中,然後處理任務。
onStartCommand
爲了驗證我們的猜想。繼續閱讀源碼,找到onStartCommand
。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
可以看出,內部調用了onStart
。
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
在onStart
內部,終於驗證了我們的猜想。將intent
和startId
,發送到了子線程中。於是我們找到Handler內部的handleMessage方法一探究竟。
Handler內部的handleMessage
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
看到這裏,終於明白了onHandleIntent
爲什麼不需再開線程。也明白了爲什麼可以自動銷燬Service。
但是,機智的你可能心裏在嘀咕。那個HandlerThread
呢?裏面是個死循環,不能自動停止,一直佔有着資源怎麼辦?那麼。我們再來看一下Service的最後一個生命週期。
onDestroy
@Override
public void onDestroy() {
mServiceLooper.quit();
}
可以看出,在銷燬的同時也退出了Looper
循環。
最後
- IntentService構造方法傳的那個參數是來幹嘛的
我們先來看一下IntentService的構造函數。可以看出最終傳給了HandlerThread。而給Thread傳一個名字更加便於調試,不然線程的默認名是"Thread-" + id;
,不方便調試。
public IntentService(String name) {
super();
mName = name;
}
@Override
public void onCreate() {
//..
//省略了部分源碼
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
//..
//省略了部分源碼
}
- 爲什麼不在
onHandleIntent
中退出Looper?
那是因爲跟普通Service一樣,IntentService也可以啓動多次。如果在結束服務之前又有新任務提交過來了,stopSelf(int startId)
並不能結束服務。因爲startId
必須與最後一次啓動相同時纔會結束服務。所以在onDestory中退出更加合適。
總結
可以看出在IntentService的內部又出現了Handler的身影,Handler在Android中的地位不言而喻,對於Handler不太瞭解的,可以參考這篇 Handler消息機制 源碼解讀。