@author:小馬快跑 @email:mqcoder90@gmail.com @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執行緒沒有任務時不會再阻塞而是退出了。