IntentService源碼分析

目錄介紹

  • 1.IntentService的做用
  • 2.IntentService使用場景
  • 3.IntentService使用步驟
  • 4.IntentService源碼分析php

    • 4.1 總體源碼展現及概括
    • 4.2 如何單獨開啓1個新的工做線程
    • 4.3 IntentService如何將Intent傳遞給服務而且依次插入到工做隊列中
  • 5.IntentService與其餘線程對比
  • 6.問題答疑解答

0.問題答疑

  • 0.0.1 IntentService源碼是如何設計的?原理是什麼,有什麼樣的特色呢?
  • 0.0.2 IntentService內部源碼handler爲什麼不會阻塞線程?
  • 0.0.3 若是啓動IntentService屢次,會出現什麼狀況呢?
  • 0.0.4 IntentService是如何單獨開啓1個新的工做線程?執行該線程有何特色?
  • 0.0.5 若是intentService啓動屢次,那麼IntentService如何經過onStartCommand()將Intent傳遞給服務 & 依次插入到工做隊列中
  • 0.0.6 屢次開啓intentService,那爲何工做任務隊列是順序執行的?
  • 0.0.7 爲何不建議經過 bindService()啓動IntentService,而是直接start開啓service?

1.IntentService的介紹和做用

  • IntentService的介紹android

    • IntentService是本身維護了一個線程,來執行耗時的操做,而後裏面封裝了HandlerThread,可以方便在子線程建立Handler。
    • IntentService是繼承自Service用來處理異步請求的一個基類,客戶端startService發送請求,IntentService就被啓動,而後會在一個工做線程中處理傳遞過來的Intent,當任務結束後就會自動中止服務。
  • IntentService的做用git

    • 開啓多線程
    • 執行異步請求邏輯

2.IntentService使用場景

  • IntentService不須要咱們本身去關閉Service,它本身會在任務完成以後自行關閉,不過每次只能處理一個任務,因此不適用於高併發,適用於請求數較少的狀況
  • 1.相似於APP的版本檢測更新,後臺定位功能以及讀取少許的IO操做。
  • 2.線程任務需按順序、在後臺執行,好比阿里雲推送的服務service就是繼承IntentSerVice
  • 3.將部分application初始化的邏輯放到intentService裏面處理,能夠提升application啓動時間
目前,因爲正式項目中application初始化工做太多,因此決定分擔部分邏輯到IntentService中處理
好比:如今application初始化內容有:數據庫初始化,阿里雲推送初始化,騰訊bugly初始化,im初始化,神策初始化,內存泄漏工具初始化,頭條適配方案初始化,阿里雲熱修復……等等。將部分邏輯放到IntentService中處理,能夠縮短不少時間。

3.IntentService使用步驟

  • 步驟1:定義InitializeService類,而且須要繼承IntentService的子類github

    • 需 傳入線程名稱、複寫onHandleIntent()方法
  • 步驟2:在清單文件Manifest.xml中註冊服務
  • 步驟3:開啓該IntentService服務,並在onHandleIntent方法中作相關操做
//第一步
InitializeService.start(this);
//第二步
<service android:name=".InitializeService"/>
//第三步
public class InitializeService extends IntentService {

    private static final String ACTION_INIT = "initApplication";

    public static void start(Context context) {
        Intent intent = new Intent(context, InitializeService.class);
        intent.setAction(ACTION_INIT);
        context.startService(intent);
    }
    
    public InitializeService(){
        //注意這裏須要寫類的名稱
        super("InitializeService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_INIT.equals(action)) {
                initApplication();
            }
        }
    }

    private void initApplication() {
        //處理耗時操做和避免在application作過多初始化工做,好比初始化數據庫等等
    }
}

4.IntentService源碼分析

4.1 總體源碼展現及概括

  • IntentService實際上內部實例化了一個HandlerThread,而且封裝了一個Handler,因此他的工做流程經過上面的源碼,分析以下:數據庫

    • 建立一個HandlerThread,開啓HandlerThread來建立Looper
    • 建立一個Handler,傳入Looper,從而在子線程實例化Handler
    • 在onStartCommand中獲取到的Intent做爲消息的obj發送出去
    • 而後在onHandleIntent中處理這個消息,注意此時是在子線程
    • 跟HandlerThread同樣,IntentService內部是採用Handler來實現的,因此任務是串行執行的,不適用於大量耗時操做。
  • 源碼以下所示:
/**
 * <pre>
 *     @author yangchong
 *     blog  : https://github.com/yangchong211
 *     time  : 2017/01/22
 *     desc  : 初始化工做,子線程,處理耗時操做和避免在application作過多初始化工做,好比初始化數據庫等等
 *     revise:
 * </pre>
 */
public abstract class IntentService extends Service {

    //子線程中的Looper
    private volatile Looper mServiceLooper;
    //內部持有的一個mServiceHandler對象
    private volatile ServiceHandler mServiceHandler;
    //內部建立的線程名字
    private String mName;
    //服務被異常終止後從新建立調用onStartCommand是否回傳Intent
    private boolean mRedelivery;

    /**
     * 內部建立了一個ServiceHandler,而後將傳遞過來的Intent封裝成一個Message,
     * 而後再將Message封裝成一個Intent,回調onHandleIntent,其實轉換的目的就是
     * 將主線程的Intent切換到子線程中去執行了而已。
     */
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            //處理髮送過來的消息,在子線程
            onHandleIntent((Intent)msg.obj);
            //處理完消息以後中止Service
            stopSelf(msg.arg1);
        }
    }

    /**
     * 工做線程的名字
     * @param name                      name
     */
    public IntentService(String name) {
        super();
        mName = name;
    }

    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //建立HandlerThread
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        //開啓線程建立子線程Looper
        thread.start();
        //獲取子線程Looper
        mServiceLooper = thread.getLooper();
        //建立子線程Handler
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        //建立一個Message
        Message msg = mServiceHandler.obtainMessage();
        //消息標誌,做爲當前Service的標誌
        msg.arg1 = startId;
        //攜帶Intent
        msg.obj = intent;
        //發送消息,此時將線程切換到子線程
        mServiceHandler.sendMessage(msg);
    }


    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        //調用onStart方法
        onStart(intent, startId);
        //根據mRedelivery的值來肯定返回重傳Intent的黏性廣播仍是非黏性廣播
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        //退出Looper
        mServiceLooper.quit();
    }


    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

    /*子類必須實現的抽象方法*/
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

4.2 如何單獨開啓1個新的工做線程

  • 在onCreate()方法中
// IntentService源碼中的 onCreate() 方法
@Override
public void onCreate() {
    super.onCreate();
    // HandlerThread繼承自Thread,內部封裝了 Looper
    //經過實例化andlerThread新建線程並啓動
    //因此使用IntentService時不須要額外新建線程
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

    //得到工做線程的 Looper,並維護本身的工做隊列
    mServiceLooper = thread.getLooper();
    //將上述得到Looper與新建的mServiceHandler進行綁定
    //新建的Handler是屬於工做線程的。
    mServiceHandler = new ServiceHandler(mServiceLooper);
}

private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
        super(looper);
    }

//IntentService的handleMessage方法把接收的消息交給onHandleIntent()處理
//onHandleIntent()是一個抽象方法,使用時須要重寫的方法
    @Override
    public void handleMessage(Message msg) {
        // onHandleIntent 方法在工做線程中執行,執行完調用 stopSelf() 結束服務。
        onHandleIntent((Intent)msg.obj);
        //onHandleIntent 處理完成後 IntentService會調用 stopSelf() 自動中止。
        stopSelf(msg.arg1);
    }
}

//onHandleIntent()是一個抽象方法,使用時須要重寫的方法
@WorkerThread
protected abstract void onHandleIntent(Intent intent);

4.3 IntentService如何將Intent傳遞給服務而且依次插入到工做隊列中

public int onStartCommand(Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

public void onStart(Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
//把 intent 參數包裝到 message 的 obj 中,而後發送消息,即添加到消息隊列裏
//這裏的Intent 就是啓動服務時startService(Intent) 裏的 Intent。
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

//清除消息隊列中的消息
@Override
public void onDestroy() {
    mServiceLooper.quit();
}

5.IntentService與其餘線程對比

  • IntentService內部採用了HandlerThread實現,做用相似於後臺線程;
  • 與後臺線程相比,IntentService是一種後臺服務,優點是:優先級高(不容易被系統殺死),從而保證任務的執行
  • 對於後臺線程,若進程中沒有活動的四大組件,則該線程的優先級很是低,容易被系統殺死,沒法保證任務的執行

6.問題答疑解答

0.0.1 IntentService源碼是如何設計的?原理是什麼,有什麼樣的特色呢?

0.0.2 IntentService內部源碼handler爲什麼不會阻塞線程?

0.0.3 若是啓動IntentService屢次,會出現什麼狀況呢?

  • IntentService屢次被啓動,那麼onCreate()方法只會調用一次,因此只會建立一個工做線程。可是會調用屢次onStartCommand方法,只是把消息加入消息隊列中等待執行

0.0.4 IntentService是如何單獨開啓1個新的工做線程?執行該線程有何特色?

  • 看4.2源碼分析

0.0.5 若是intentService啓動屢次,那麼IntentService如何經過onStartCommand()將Intent傳遞給服務 & 依次插入到工做隊列中

  • 看4.3源碼分析

0.0.6 屢次開啓intentService,那爲何工做任務隊列是順序執行的?

  • 結論:若是一個任務正在IntentService中執行,此時你再發送一個新的任務請求,這個新的任務會一直等待直到前面一個任務執行完畢纔開始執行
  • 分析:segmentfault

      1. 因爲onCreate() 方法只會調用一次,因此只會建立一個工做線程;
      1. 當屢次調用 startService(Intent) 時(onStartCommand也會調用屢次)其實並不會建立新的工做線程,只是把消息加入消息隊列中等待執行,因此,屢次啓動 IntentService 會按順序執行事件
      1. 若是服務中止,會清除消息隊列中的消息,後續的事件得不到執行。

0.0.7 爲何不建議經過 bindService()啓動IntentService,而是直接start開啓service?

  • 首先先看看IntentService源代碼
@Override
public IBinder onBind(Intent intent) {
    return null;
}
  • 在IntentService中,onBind()是默認返回null的,而採用bindService() 啓動 IntentService的生命週期是:onCreate() —>onBind()—>onunbind()—>onDestory()

並不會調用onstart()或者onstartcommand()方法,因此不會將消息發送到消息隊列,那麼onHandleIntent()將不會回調,即沒法實現多線程的操做。多線程

關於其餘內容介紹

01.關於博客彙總連接

02.關於個人博客

相關文章
相關標籤/搜索