2. 應用程式子線程消息循環模型
在Java架構中,如果我們想在目前應用程式中建立一個子線程,一般就是通過自己實作一個類,這個類繼承于Thread類,然後重載Thread類的run函數,把我們想要在這個子線程執行的任務都放在這個run函數裡面實作。最後執行個體這個自定義的類,并且調用它的start函數,這樣一個子線程就建立好了,并且會調用這個自定義類的run函數。但是當這個run函數執行完成後,子線程也就結束了,它沒有消息循環的概念。
前面說過,有時候我們需要在應用程式中建立一些常駐的子線程來不定期地執行一些計算型任務,這時候就可以考慮使用Android系統提供的HandlerThread類了,它具有建立具有消息循環功能的子線程的作用。
HandlerThread類實作在frameworks/base/core/java/android/os/HandlerThread.java檔案中,這裡我們通過使用情景來有重點的分析它的實作。
在前面一篇文章Android系統預設Home應用程式(Launcher)的啟動過程源代碼分析中,我們分析了Launcher的啟動過程,其中在Step 15(LauncherModel.startLoader)和Step 16(LoaderTask.run)中,Launcher會通過建立一個HandlerThread類來實作在一個子線程加載系統中已經安裝的應用程式的任務:
public class LauncherModel extends BroadcastReceiver {
......
private LoaderTask mLoaderTask;
private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
static {
sWorkerThread.start();
}
private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
public void startLoader(Context context, boolean isLaunching) {
......
synchronized (mLock) {
......
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
......
mLoaderTask = new LoaderTask(context, isLaunching);
sWorker.post(mLoaderTask);
}
}
}
private class LoaderTask implements Runnable {
public void run() {
keep_running: {
......
// second step
if (loadWorkspaceFirst) {
......
loadAndBindAllApps();
} else {
}
}
}
在這個LauncherModel類中,首先建立了一個HandlerThread對象:
private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
接着調用它的start成員函數來啟動一個子線程:
static {
sWorkerThread.start();
接着還通過這個HandlerThread對象的getLooper函數來獲得這個子線程中的消息循環對象,并且使用這個消息循環建立對象來建立一個Handler:
private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
有了這個Handler對象sWorker之後,我們就可以往這個子線程中發送消息,然後在處理這個消息的時候執行加載系統中已經安裝的應用程式的任務了,在startLoader函數中:
mLoaderTask = new LoaderTask(context, isLaunching);
sWorker.post(mLoaderTask);
這裡的mLoaderTask是一個LoaderTask對象,它實作了Runnable接口,是以,可以把這個LoaderTask對象作為參數傳給sWorker.post函數。在sWorker.post函數裡面,會把這個LoaderTask對象封裝成一個消息,并且放入這個子線程的消息隊列中去。當這個子線程的消息循環處理這個消息的時候,就會調用這個LoaderTask對象的run函數,是以,我們就可以在LoaderTask對象的run函數中通過調用loadAndBindAllApps來執行加載系統中已經安裝的應用程式的任務了。
了解了HanderThread類的使用方法之後,我們就可以重點地來分析它的實作了:
public class HandlerThread extends Thread {
private Looper mLooper;
public HandlerThread(String name) {
super(name);
......
public void run() {
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
......
}
Looper.loop();
public Looper getLooper() {
return mLooper;
首先我們看到的是,HandlerThread類繼承了Thread類,是以,通過它可以在應用程式中建立一個子線程,其次我們看到在它的run函數中,會進入一個消息循環中,是以,這個子線程可以常駐在應用程式中,直到它接收收到一個退出消息為止。
在run函數中,首先是調用Looper類的靜态成員函數prepare來準備一個消息循環對象:
Looper.prepare();
然後通過Looper類的myLooper成員函數将這個子線程中的消息循環對象儲存在HandlerThread類中的成員變量mLooper中:
mLooper = Looper.myLooper();
這樣,其它地方就可以友善地通過它的getLooper函數來獲得這個消息循環對象了,有了這個消息循環對象後,就可以往這個子線程的消息隊列中發送消息,通知這個子線程執行特定的任務了。
最在這個run函數通過Looper類的loop函數進入消息循環中:
Looper.loop();
這樣,一個具有消息循環的應用程式子線程就準備就緒了。
HandlerThread類的實作雖然非常簡單,當然這得益于Java提供的Thread類和Android自己本身提供的Looper類,但是它的想法卻非常周到,為應用程式開發人員提供了很大的友善。