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类,但是它的想法却非常周到,为应用程序开发人员提供了很大的方便。