HandlerThread和IntentService源碼解析

簡介

首先我們先來了解HandlerThread和IntentService是什么,以及為什么要將這兩者放在一起分析。
HandlerThread:

HandlerThread 其實是Handler + Thread + Looper的組合,它本質(zhì)上是一個Thread,因為它繼承了Thread。相比普通的Thread,它不會堵塞,因為他內(nèi)部通過Looper實現(xiàn)了消息循環(huán)機制,保證了多個任務的串行執(zhí)行。缺點是效率比較低,因為,串行執(zhí)行比起并行執(zhí)行,效率肯定會比較較低。

IntentService:

IntentService是繼承并處理異步請求的一個類,其本質(zhì)上是一個Service,因為他繼承了Service,所以開啟IntentService和普通的Service一致。但是他和普通的IntentService不同之處在于,他可以處理異步任務,在任務處理完成之后會自動結(jié)束Service。另外我們可以啟動IntentService多次,而每一個耗時任務會已工作隊列的方式在IntentService的onHandleIntent回調(diào)方法中執(zhí)行,并且是串行執(zhí)行的。

好了在了解HandlerThread和IntentService分別是什么之后,我們來解決第二個問題,那就是為什么我要將2者放在一起分析?其實IntentService的內(nèi)部是通過HandlerThread和Handler來實現(xiàn)異步操作的,當我們了解了HandlerThread的使用和原理之后,再去理解IntentService就會容易的多。好的,下面讓我們開始HandlerThread的源碼之旅。

HandlerThread的使用和原理

HandlerThread的使用

這里我們要實現(xiàn)一個每隔5s更新TextView中的值的一個demo,源碼如下:

public class MainActivity extends AppCompatActivity {
  private static final String TAG = "MainActivity";
  private TextView mTv;
  private Button mBtn;
  HandlerThread mHandlerThread;
  Handler mThreadHandler;

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    initView();
    mBtn.setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View v) {
        initThread();
      }
    });
  }

  private void initView() {
    mTv = (TextView) findViewById(R.id.tv);
    mBtn = (Button) findViewById(R.id.btn);
  }

  private void initThread() {
    //創(chuàng)建一個HandlerThread并開啟線程
    mHandlerThread = new HandlerThread("update-msg");
    mHandlerThread.start();

    //從mHandlerThread中得到Looper并創(chuàng)建Handler
    mThreadHandler = new Handler(mHandlerThread.getLooper()) {
      @Override public void handleMessage(Message msg) {
        Log.v(TAG, "currentThread===>" + Thread.currentThread() + "   what====>" + msg.what);
        try {
          update();
        } catch (Exception e) {
          e.printStackTrace();
        }
        mThreadHandler.sendEmptyMessage(200);
      }
    };
    mThreadHandler.sendEmptyMessage(200);
  }

  private void update() throws Exception {
    Thread.sleep(3000);
    runOnUiThread(new Runnable() {
      @Override public void run() {
        String result = "每隔3s更新一次:";
        result += Math.random();
        mTv.setText(result);
      }
    });
  }
}

輸出的日志如下:

currentThread===>Thread[update-msg,5,main] what====>200

從日志我們可以看出handleMessage運行在我們創(chuàng)建的HandlerThread("update-msg")之下。我們有理由懷疑這跟我們傳入的mHandlerThread.getLooper()有關(guān)。我們的mThreadHandler 是在UI線程中創(chuàng)建的,按理來說handleMessage應該運行在UI線程中才對。了解Handler原理的都知道,handleMessage方法是在Handler的dispatchMessage方法中被調(diào)用的且dispatchMessage方法沒有進行線程切換。所以線程切換應該發(fā)生在dispatchMessage被調(diào)用的地方,那dispatchMessage是在哪被調(diào)用的呢?我們發(fā)現(xiàn)Loop的loop()方法中調(diào)用了dispatchMessage()方法。(這里我就不貼Handler和Loop相關(guān)的代碼,感興趣的可以參考我以前的文章:Handler的原理)而且我們還發(fā)現(xiàn)了loop()方法的注釋如下:

image.png

意思是loop()方法運行在Loop被綁定的線程中。

那Loop又是在什么時候被綁定的呢?

image.png

就是這2個方法對Loop進行了綁定。那這個sThreadLocal又是什么鬼?它到底有什么用?別急,我們?nèi)タ聪滤鼊?chuàng)建的地方:
image.png

它其實就是一個ThreadLocal,關(guān)于ThreadLocal的原理,大家可以參考:ThreadLocal源碼深入分析
在這里我簡單的說下,其實ThreadLocal的作用,就是通過Thread中的threadLocals:ThreadLocalMap變量將我們通過ThreadLocal#set方法傳進來的數(shù)據(jù)跟Thread進行綁定,從而保證了訪問到的變量屬于當前線程,每個線程都保存有一個變量副本,每個線程的變量都不同,而同一個線程在任何時候訪問這個本地變量的結(jié)果都是一致的。當此線程結(jié)束生命周期時,所有的線程本地實例都會被GC。ThreadLocal相當于提供了一種線程隔離,將變量與線程相綁定。ThreadLocal通常定義為private static類型。

通過上面的分析,我們已經(jīng)知道了Handler#handleMessage方法會運行在Loop說綁定的線程上,驗證了我們一開始的猜想。這里Loop是從我們創(chuàng)建的HandlerThread中得到的,而HandlerThread其實就是一個線程,所以Loop綁定在了新創(chuàng)建的HandlerThread上。但是我們并不滿足于此,我們得進一步看看HandlerThread和普通的Thread到底有什么不一樣。

HandlerThread的源碼解析

HandlerThread的代碼量其實并不多,它繼承于Thread,主要的方法其實就是run方法

@Override
    public void run() {
        mTid = Process.myTid();
        //創(chuàng)建Loop并綁定當前線程
        Looper.prepare();
        //關(guān)鍵代碼
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
      //設(shè)置線程優(yōu)先級
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

短短的幾行代碼確幾乎實現(xiàn)了我們想要的所有功能,我們來看關(guān)鍵代碼run方法中的synchronized 代碼塊其實對應于getLooper方法中的synchronized代碼塊,這樣做的目的是為了確保,我們通過getLoop()方法得到的Loop對象一定是被初始化后的Loop。當Loop被初始化以后會調(diào)用抽象方法onLooperPrepared(),他一般被用于在開啟隊列循環(huán)之前做一些初始化的操作,然后執(zhí)行任務隊列。

總結(jié)

HandlerThread的原理已經(jīng)分析完了,我們來總結(jié)一下它的特點:

1.HandlerThread它就是一個線程,和開啟普通的線程得到操作一致
2.HandlerThread需要搭配Handler使用,單獨使用的意義不大
3.HandlerThread會將通過handleMessage傳遞進來的任務進行串行執(zhí)行,這是由messageQueue的特性決定的,從而也說明了HandlerThread效率相比并行操作會比較低

IntentService的使用和原理

分析完HandlerThread之后我們來分析一下IntentService的使用和原理,老規(guī)矩我們先看怎么使用。

IntentService的使用

public class MyIntentService extends IntentService {
  private static final String TAG = "MyIntentService";

  public MyIntentService() {
    super("MyIntentService");
    Log.v(TAG,
        "MyIntentService===>MyIntentService()" + "  currentThread==>" + Thread.currentThread());
  }

  /**
   * Creates an IntentService.  Invoked by your subclass's constructor.
   *
   * @param name Used to name the worker thread, important only for debugging.
   */
  public MyIntentService(String name) {
    super(name);
    Log.v(TAG,
        "MyIntentService===>MyIntentService(name)" + "  currentThread==>" + Thread.currentThread());
  }

  @Override public void onCreate() {
    super.onCreate();
    Log.v(TAG, "MyIntentService===>onCreate" + "  currentThread==>" + Thread.currentThread());
  }

  @Override protected void onHandleIntent(@Nullable Intent intent) {
    Log.v(TAG, "MyIntentService===>onHandleIntent" + "  currentThread==>" + Thread.currentThread());
    try {
      Thread.sleep(10000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  @Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    Log.v(TAG, "MyIntentService===>onStartCommand" + "  currentThread==>" + Thread.currentThread());
    return super.onStartCommand(intent, flags, startId);
  }
}

調(diào)用服務和我們普通的Service一致
輸出的日志如下

MyIntentService===>MyIntentService() currentThread==>Thread[main,5,main]
MyIntentService===>onCreate currentThread==>Thread[main,5,main]
MyIntentService===>onStartCommand currentThread==>Thread[main,5,main]
MyIntentService===>onHandleIntent currentThread==>Thread[IntentService[MyIntentService],5,main]

從中我們可以看出onHandleIntent方法是運行在子線程中的,更有意思的是,當我們在onHandleIntent 方法中執(zhí)行延遲操作時,打印的日志如下描述:
1、當服務沒執(zhí)行完時又點擊了開啟服務的操作,此時,onStartCommand方法會立即執(zhí)行,而onHandleIntent方法會在上一個任務執(zhí)行完以后再去執(zhí)行onHandleIntent方法。
2、當服務已經(jīng)執(zhí)行完被自動結(jié)束以后,再去調(diào)用service,輸出的日志和第一次輸出的日志一致。

可能我說的比較抽象,大家自取去操作一遍就會發(fā)現(xiàn)我所說的有意思的地方。從上面的日志輸出,我們可以得出以下結(jié)論:
1、IntentService在任務執(zhí)行完以后會自動結(jié)束
2、IntentService接收的任務是串行執(zhí)行的,并且互不干擾
3、IntentService的生命周期和普通的Service一致,只不過多了一個onHandleIntent回調(diào)方法,并且它是串行回調(diào)的,等待上一個任務執(zhí)行完以后才會再次被調(diào)用

但是為什么會這樣呢?大家有沒有想過。當然,所有的答案都隱藏在源碼里,讓我們一起去揭開他神秘的面紗吧。

IntentService源碼解析

首先我們先來看下IntentService的幾個成員變量,如下圖所示:


image.png

關(guān)于Loop和Handler我們都很熟悉了,前者是遍歷消息隊列的消息泵后者則是處理Handler發(fā)送過來的消息的。下面我來看下他們初始化得到地方。
Loop初始化

image.png

原來他們都是在Service的onCreate回調(diào)方法中被初始化的。
通過上文HandlerThread的分析,我們知道ServiceHandler的handleMessage方法會運行在mServiceLooper綁定的指定線程上。這里這也就驗證了我們上文日志的輸出。

下面我們來解決另外一個問題,也就是IntentService的生命周期函數(shù)的執(zhí)行情況。
請看下面的代碼:


image.png

我們都知道當服務被啟動以后,再次調(diào)用服務的時候都會回調(diào)onStartCommand方法,onStartCommand又調(diào)用了onStart方法,而onStart方法中只是通過Handler發(fā)送一個異步消息,然后ServiceHandler的handleMessage收到消息以后調(diào)用了onHandleIntent,這也就驗證了上文的日志輸出。

下面我們來重點分析一下Service的stopSelf()方法,他有兩個重載方法,一個有參,一個無參,那他們之間有什么不同呢?
我們還是通過源碼來看一下吧。


image.png

可以看到無參方法只是簡單的調(diào)用了有參方法,并傳入了一個-1的參數(shù)。所以我們只有直接分析有參的方法就可以。
由于Android sdk并沒有開放ActivityManageProxy(我們知道ActivityManage在客戶端得到代理是ActivityManageProxy)的代碼,所以我們只能通過查找相關(guān)資料來解決我們的疑惑。
最終我在官網(wǎng)上得到的答案如下:


image.png

簡單來說就是stopSelf中的startId對應于onStartCommand中的startId,當stopSelf(startId)中的startId等于onStartCommand中的最后一個進來的startId的時候,就代表消息隊列中沒有更多的消息需要處理了,所以執(zhí)行完當前的消息以后,會去執(zhí)行Service的stop操作

總結(jié)

關(guān)于IntentService的分析到這就告一段落了,其實IntentService就是基于HandlerThread機制來實現(xiàn)的,它允許我們在onHandleIntent回調(diào)方法中執(zhí)行異步操作。同時要注意他的生命周期回調(diào)函數(shù)的差異。下面貼上官網(wǎng)上關(guān)于IntentService類的介紹,幫助大家理解。


image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容