Android多線程之IntentService


@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使用例子

先上效果圖:
IntentService.gif
可以看到,我們先啓動了第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線程沒有任務時不會再阻塞而是退出了。

發佈了53 篇原創文章 · 獲贊 98 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章