Android 多線程開發的演變
因為 Android 是使用 Java 開發的,是以當我們談及 Android 中的多線程,必然繞不過 Java 中的多線程程式設計。但在這篇文章中,我們不會過多地分析 Java 中的多線程程式設計的知識。我們會在以後分析 Java 并發程式設計的時候分析 Java 中的多線程、線程池和并發 API 的用法。
我們先來總結一下 Android 多線程程式設計的演變過程:
首先是 Java 的 Thread。因為本身在建立一個線程和銷毀一個線程的時候會有一定的開銷,當我們任務的執行時間相比于這個開銷很小的時候,單獨建立一個線程就顯得不劃算。是以,當程式中存在大量的、小的任務的時候,建議使用線程池來進行管理。
但我們一般也很少主動去建立線程池,這是因為——也許是考慮到開發者自己去維護一個線程池比較複雜—— Android 中已經為我們設計了 AsyncTask。AsyncTask 内部封裝了一個線程池,我們可以使用它來執行耗時比較短的任務。但 AsyncTask 也有一些缺點:
- 如果你的程式中存在很多的不同的任務的時候,你可能要為每個任務定義一個 AsyncTask 的子類。
- 從異步線程切換到主線程的方式不如 RxJava 簡潔。
是以,在實際開發的過程中,我通常使用 RxJava 來實作異步的程式設計。尤其是局部的優化、不值得專門定義一個 AsyncTask 類的時候,RxJava 用起來更加舒服。
上面的多線程建立的隻是普通的線程,對系統來說,優先級比較低。在 Android 中還提供了 IntentService 來執行優先級相對較高的任務。啟動一個 IntentService 任務的時候會将任務添加到其内部的、異步的消息隊列中執行。此外,IntentService 又繼承自 Service,是以這讓它具有異步和較高的優先級兩個優勢。
在之前的文章中,我們已經分析過 AsyncTask、RxJava 以及用來實作線程切換的 Handler. 這裡奉上這些文章的連結:
- 《Android AsyncTask 源碼分析》
- 《RxJava2 系列-1:一篇的比較全面的 RxJava2 方法總結》
- 《RxJava2 系列-2:背壓和Flowable》
- 《RxJava2 系列-3:使用 Subject》
在這裡我們要分析 IntentService 和 HandlerThread,它們都會使用到 Android 中的 Handler 機制,你可以通過下面的文章來進行了解:
《Android 消息機制:Handler、MessageQueue 和 Looper》
1、異步消息隊列:HandlerThread
如果你之前還沒有了解過 Handler 的實作的話,那麼最好通過我們上面的那篇文章 《Android 消息機制:Handler、MessageQueue 和 Looper》 了解一下。因為 HandlerThread 就是通過封裝一個 Looper 來實作的。
1.1 HandlerThread 的使用
HandlerThread 繼承自線程類 Thread,内部又維護了一個 Looper,Looper 内又維護了一個消息隊列。是以,我們可以使用 HandlerThread 來建立一個異步的線程,然後不斷向該線程發送任務。這些任務會被封裝成消息放進 HandlerThread 的消息隊列中被執行。是以,我們可以用 HandlerThread 來建立異步的消息隊列。
在使用 HandlerThread 的時候有兩個需要注意的地方:
- 因為 HandlerThread 内部的 Looper 的初始化和開啟循環的過程都在
方法中執行,是以,在使用 HandlerThread 之前,你必須調用它的 start() 方法。run()
- 因為 HandlerThread 的
方法使用 Looper 開啟一個了無限循環,是以,當不再使用它的時候,應該調用它的run()
或quitSafely()
方法來結束該循環。quit()
在使用 HandlerThread 的時候隻需要建立一個它的執行個體,然後使用它的 Looper 來建立 Handler 執行個體,并通過該 Handler 發送消息來将任務添加到隊列中。下面是一個使用示例:
myHandlerThread = new HandlerThread("MyHandlerThread");
myHandlerThread.start();
handler = new Handler( myHandlerThread.getLooper() ){
@Override
public void handleMessage(Message msg) {
// ... do something
}
};
handler.sendEmptyMessage(1);
這裡我們建立了 HandlerThread 執行個體之後用它來建立 Handler 然後通過 Handler 把任務加入到消息隊列中進行執行。
顯然,使用 HandlerThread 可以很輕松地實作一個消息隊列。你隻需要在建立了 Handler 之後向它發送消息,然後所有的任務将被加入到隊列中執行。當然,它也有缺點。因為所有的任務将會被按順序執行,是以一旦隊列中有某個任務執行時間過長,那麼就會導緻後續的任務都會被延遲處理。
1.2 HandlerThread 源碼解析
下面是該 API 的源碼,實作也比較簡單,我們直接通過注釋來對主要部分進行說明:
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
protected void onLooperPrepared() { }
// 在這個方法開啟了 Looper 循環,因為是一個無限循環,是以不适用的時候應該将其停止
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
// 擷取該 HandlerThread 對應的 Looper
public Looper getLooper() {
if (!isAlive()) {
return null;
}
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) { }
}
}
return mLooper;
}
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
// ... 無關代碼
}
上面的代碼比較簡單明了,在 run() 方法中初始化 Looper 并執行。如果 Looper 還沒有被建立,那麼當調用
getLooper()
方法擷取 Looper 的時候會讓線程阻塞。當 Looper 建立完畢之後會喚醒所有阻塞的線程繼續執行。另外,就是兩個停止 Looper 的方法。它們基本上就是對 Looper 進行了一層封裝。
2、IntentService
2.1 使用 IntentService
IntentService 繼承自 Serivce,是以它比普通的多線程任務優先級要高。這使得它相比于普通的異步任務不容易被系統 kill 掉。它内部也是通過一個 Looper 來實作的,是以也是一種消息隊列。在研究它的源碼之前,我們先來看一下它的使用。
IntentService 的使用是比較簡單的,隻需要:1).繼承它并實作其中的
onHandleIntent()
方法;2). 将 IntentService 注冊到 manifest 中;3). 像開啟一個普通的服務那樣開啟一個 IntentService 即可。下面是該類的一個使用示例:
public class FileRecognizeTask extends IntentService {
public static void start(Context context) {
Intent intent = new Intent(context, FileRecognizeTask.class);
context.startService(intent);
}
public FileRecognizeTask() {
super("FileRecognizeTask");
}
@Override
protected void onHandleIntent(@androidx.annotation.Nullable @Nullable Intent intent) {
// 你的需要異步執行的業務邏輯
}
}
OK,介紹完了 IntentService 的使用,我們再來分析一下它的源碼。
2.2 IntentService 源碼分析
實作 IntentService 的時候使用到了我們上面分析過的 HandlerThread. 首先,在
onCreate()
回調方法中建立了一個 HandlerThread,然後使用它的 Looper 建立了一個 ServiceHandler:
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
ServiceHandler 是 IntentSerice 的内部類,其定義如下:
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);
}
}
ServiceHandler 用來執行被添加到隊列中的消息。它會回調 IntentService 中的
onHandleIntent()
方法,也就是我們實作業務邏輯的方法。當消息執行完畢之後,會調用 Service 的
stopSelf(int)
方法來嘗試停止服務。注意這裡調用的是
stopSelf(int)
而不是
stopSelf()
。它們之間的差別是,當還存在沒有完成的任務的時候
stopSelf(int)
不會立即停止服務,而
stopSelf()
方法會立即停止服務。
IntentSerivce 的
onCreate()
方法會在第一次啟動的時候被調用,來建立服務。而
onStartCommond()
方法會在每次啟動的時候被調用。下面是該方法的定義。
@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;
}
onStartCommand()
内部調用了
onStart()
來處理請求。在
onStart()
方法中會通過 mServiceHandler 建立一個消息,并将該消息發送給 mServiceHandler. 該消息會在被 mServiceHandler 放進消息隊列中排隊,并在合适的時機被執行。
是以,我們可以總結一下 IntentService 的工作過程:首先,當我們第一次啟動 IntentService 的時候會初始化一個 Looper 和 Handler;然後調用它的 onStartCommond() 方法,把請求包裝成消息之後發送到消息隊列中等待執行;當消息被 Handler 處理的時候會回調 IntentService 的
onHandleIntent()
方法來執行。此時,如果又有一個任務需要執行,那麼 IntentService 的 onStartCommond() 方法會再次被執行并把請求封裝之後放入隊列中。當隊列中的所有的消息都執行完畢,并且沒有新加入的請求,那麼此時服務就會自動停止,否則服務還會繼續在背景執行。
這裡,同樣也應該注意下,IntentService 中的任務是按照被添加的順序來執行的。
總結
以上就是我們對 IntentService 和 HandlerThread 的分析。它們都是使用了 Handler 來實作,是以搞懂它們的前提是搞懂 Handler。關于 Handler,還是推薦一下筆者的另一篇文章 《Android 消息機制:Handler、MessageQueue 和 Looper》。
我是 WngShhng. 如果您喜歡我的文章,可以在以下平台關注我:
- 個人首頁:https://shouheng88.github.io/
- 掘金:https://juejin.im/user/585555e11b69e6006c907a2a
- Github:https://github.com/Shouheng88
- CSDN:https://blog.csdn.net/github_35186068
- 微網誌:https://weibo.com/u/5401152113