@author:小馬快跑
@email:[email protected]
@github:https://github.com/crazyqiang
IntentService是什麼?
IntentService繼承自Service,所以IntentService也是四大組件之一,IntentService內部封裝了HandlerThread線程 (只有一個線程) 來按順序處理異步任務,通過startService(Intent) 來啓動IntentService並通過Intent來傳遞異步任務,當任務結束後IntentService通過stopSelf(int startId)來自己停止服務。IntentService是一個抽象類,如果想使用IntentService,首先創建一個類繼承IntentService,然後重寫onHandleIntent(Intent)在子線程中處理Intent傳過來的任務。
IntentService特點:
- onHandleIntent(Intent)發生在子線程,不能直接更新UI,需要先把結果發到Activity中
- 提交的任務順序執行,如果一個任務A正在IntentService中執行,此時發送另一個異步任務B到IntentService中,那麼必須等到任務A執行完之後任務B纔會開始執行
- 已經在IntentService中執行的任務是不會被打斷的
IntentService使用例子
先上效果圖:
可以看到,我們先啓動了第1個任務,當第1個任務還沒有執行完時,此時又啓動了第2個任務,第2個任務不會立即執行,而是等到第1個任務下載到100%完成之後纔會開始第2個下載任務,這就驗證了IntentService會順序執行異步任務,來看具體實現,首先繼承一個IntentService並覆寫onHandleIntent():
public class MyIntentService extends IntentService {
public static final String ACTION_ONE = "action_one";
public static final String ACTION_TWO = "action_two";
private int progressOne, progressTwo;
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (intent == null) return;
String action = intent.getAction();
switch (action) {
case ACTION_ONE:
while (progressOne < 100) {
progressOne++;
sendBroadcast(getUpdateIntent(0, progressOne));
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case ACTION_TWO:
while (progressTwo < 100) {
progressTwo++;
sendBroadcast(getUpdateIntent(1, progressTwo));
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
}
}
onHandleIntent()是在子線程中執行,通過Intent接收任務然後執行任務,並通過BroadCastReceiver把運算結果不斷髮送到Activity中來更新UI,當所有任務執行完成以後,IntentService自動關閉。我們看到在IntentService中處理了任務,那麼這裏的任務是哪裏傳過來的呢?看下面代碼:
Intent intent = new Intent(IntentServiceActivity.this, MyIntentService.class);
intent.setAction(MyIntentService.ACTION_ONE);
startService(intent);
我們看到通過startService(Intent)直接啓動並把任務傳遞到IntentService,最後別忘了在AndroidManifest.xml中定義IntentService:
<service
android:name=".multiThread.intentService.MyIntentService"
android:screenOrientation="portrait" />
完整源碼地址:Android多線程之IntentService
IntentService源碼解析
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
public IntentService(String name) {
super();
mName = name;
}
首先定義變量,並在構造方法中傳入工作線程的名字。
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
IntentService中上面三個方法的執行順序:onCreate>onStartCommand>onStart:
1、在onCreate()中初始化一個HandlerThread線程並啓動,接着初始化一個ServiceHandler並把HandlerThread中的Looper作爲參數傳入ServiceHandler,這樣就可以在主線程中通過ServiceHandler把Message發送到HandlerThread子線程中處理了;
2、在onStartCommand()中又調用了onStart()並根據mRedelivery 返回START_REDELIVER_INTENT 或者是START_NOT_STICKY,這兩個有什麼區別呢?我們來複習一下在onStartCommand()中返回值:
- START_STICKY:如果service進程被kill掉,保留service的狀態爲開始狀態,但不保留遞送的intent對象。隨後系統會嘗試重新創建service,由於服務狀態爲開始狀態,所以創建服務後一定會調用onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啓動命令被傳遞到service,那麼參數Intent將爲null。
START_NOT_STICKY:“非粘性的”。使用這個返回值時,如果在執行完onStartCommand後,服務被異常kill掉,系統不會自動重啓該服務
START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保證服務被kill後一定能重啓。
START_FLAG_REDELIVERY:如果你實現onStartCommand()來安排異步工作或者在另一個線程中工作, 那麼你可能需要使用START_FLAG_REDELIVERY來讓系統重新發送一個intent。這樣如果你的服務在處理它的時候被Kill掉, Intent不會丟失.
所以當返回START_FLAG_REDELIVERY時,如果Service被異常Kill掉,在Service重啓以後會重新發送Intent;如果返回START_NOT_STICKY,當Service被異常Kill掉時不會重新啓動。
3、在onStart()中把Intent封裝到Message中並通過ServiceHandler發送到HandlerThread中了,經過HandlerThread中的Looper.loop()循環取消息,最終還是還是ServiceHandler去處理消息,所以我們來看ServiceHandler:
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);
}
}
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
在handleMessage()中,我們發現回調了onHandleIntent()方法,而這個方法是個抽象方法,也是在子類中我們必須要實現的,所以最終消息的處理需要我們仔細去處理,注意這個回調方法是在子線程中執行的,在執行完onHandleIntent()後,調用了stopSelf來關閉自己,關閉時IntentService回調onDestroy():
@Override
public void onDestroy() {
mServiceLooper.quit();
}
我們看到在IntentService結束時調用了mServiceLooper.quit()來停止HandlerThread中Looper的循環,即HandlerThread線程沒有任務時不會再阻塞而是退出了。