在分析HandlerThread的源码之前,我们先介绍一下为什么要使用HandlerThread以及HandlerThread的简单使用方法。
1、为什么要使用HandlerThread,HandlerThread是什么?
我们都知道,通过Handler+Looper+Message可以在子线程中做相关的耗时操作,然后使用Handler发消息到主线程通知主线程刷新UI。主线程在这我们简称为UIThread。这里我们说的都是子线程做相关耗时操作,然后发消息给UIThread。但是如果我们想要在子线程和子线程之间通信,或者UIThread发消息给子线程,我们应该怎么办呢?这个时候HandlerThread的出现就是为了解决这一问题。
在Android体系中的Handler+Looper+Message里,Looper对象会在UIThread中创建,并同时创建了MessageQuene对象。接着在ActivityThread的main方法中会调用loop方法,然后进入一个死循环。其实就是创建了一个轮询机制,不断的从MessageQuene中轮询消息,一旦轮询到消息的时候,通过调用msg.target.dispatchMessage(msg)方法(msg.target其实就是Handler对象),然后最终调用了Handler中的handMessage方法处理消息。这样消息就被处理了。在这我们要注意,Looper对象和MessageQuene对象都是在UIThread线程中创建的,因此消息的处理都是在UIThread线程处理。那么当我们要在子线程和子线程直接通信,在子线程中处理消息。传统意义上的Handler+Looper+Message就没法实现了。但是我们思考一下,如果子线程中创建了Looper和MessageQuene对象的话,这个问题是不是能解决了呢?这个问题我们先抛在这。接下来我们先介绍本文要讲解的组件HandlerThread的简单使用。
2、HandlerThread的简单使用方法。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private TextView tvText;
private Handler handler;
private MyHandlerThread handlerThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvText = (TextView) findViewById(R.id.tv_text);
tvText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handlerThread = new MyHandlerThread("handlerThread");
handlerThread.start();
handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
String msgStr = (String) msg.obj;
Log.e(TAG, "handleMessage: " + Thread.currentThread().getName());
}
};
Message message = Message.obtain();
message.obj = "this is a message";
handler.sendMessage(message);
Log.e(TAG, "handleMessage: " + Thread.currentThread().getName());
}
});
}
public class MyHandlerThread extends HandlerThread {
public MyHandlerThread(String name) {
super(name);
}
@Override
public void run() {
super.run();
}
}
}
用法很简单,首先创建HandlerThread对象然后调用start方法启动当前线程。然后创建Handler对象重写handleMessage方法。创建Handler对象的时候我们要传入Looper对象,也就是通过handlerThread.getLooper()获取Looper对象。我们大概能猜到,这个Looper对象就是我们新开的子线程中的Looper对象,而不是安卓系统中创建的那个Looper对象。接着我们创建一条消息通过handler.sendMessage(message);发送消息。然后在Handler的handleMessage方法中即可拿到这条消息,接着我们可以对该消息进行处理。接下来,我们来看下handleMessage的这方法是在UIThread中执行还是子线程中执行的。我们打印一下日志:
08-22 09:49:14.802 2997-2997/com.mujin.keji.myapplication E/MainActivity: handleMessage: main
08-22 09:49:14.802 2997-3170/com.mujin.keji.myapplication E/MainActivity: handleMessage: handlerThread
从日志中可以看到,发消息是在名字为main也就是UI线程中执行的,而处理消息是在我们启动的线程名为handlerThread的线程中执行的。从而上我们就是完成了在主线程中创建了消息发到到了子线程。因为在handlerThread.start();的时候,其实就是开启了一个子线程。
3.什么时候会使用到HandlerThread呢?
- 当在开发中我们要频繁的开启子线程去做相应的操作,那么久要多次new Thread(){}.start().这样就创建了多个匿名线程,会使得程序变慢,而HandlerThread自带Looper轮询机制,使得我们可以重复的使用HandlerThread启动的线程,节省开支。
- 我们也可以在开启的子线程中手动添加Looper,但是这样以来比较麻烦。
4.HandlerThread的源码分析
/**
* Handy class for starting a new thread that has a looper. The looper can then be
* used to create handler classes. Note that start() must still be called.
*/
public class HandlerThread extends Thread {
我们首先看到HandlerThread 继承了Thread,也就是说HandlerThread 就是一个线程类,通过调用start()方法即可开启一个子线程。接着我们看下HandlerThread 的构造方法:
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
构造方法中调用了父类的构造方法,创建了一个线程类。我们在上面使用的时候,然后会调用start()方法,其实就是开启了一个线程。
handlerThread = new MyHandlerThread("handlerThread");
handlerThread.start();
我们都知道,调用start方法之后,会回调run方法,接下来我们看下HandlerThread方法做了什么操作。
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
首先调用了Looper.prepare(),这个我们很熟悉,就是为当前线程设置looper和messageQuene对象,在安卓体系中是为UI线程设置looper和messageQuene对象。但是在这里是为我们开启的子线程设置looper和messageQuene对象。然后调用Looper.loop()方法,进入轮询的状态。但是我们看到在初始化Looper mLooper 对象的时候有一个notifyAll()唤醒的操作,这是为什么呢。原因是我们在UIThread发消息给HandlerThread启动的线程的时候,由于HandlerThread在调用start()方法之后,不一定马上拿到cpu的时间片,这样一来就不会回调run方法,所有就不会永远Looper。所以就不会接收到UIThread发送的消息,我们再来看看下面的代码:
Handler handler = new Handler(handlerThread.getLooper()) {
我们创建Handler 对象的时候传入了handlerThread中的Looper对象。我们进入getLooper()方法。
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;
}
在这里我们很清晰的看到,获取Looper对象的时候,首先判断mLooper是不是空的,如果是空,wait,线程进入等待的状态。当handlerThread获取到cpu时间片,调用run方法的时候,调用了notifyAll()方法,然后再对线程进行唤醒操作。getLooper()方法继续执行,返回Looper对象,这样一来Looper就不会是空的。
上面我们就完成了对HandlerThread的源码解析,接下来我们进行小结:
- HandlerThread就是一个线程类,使用的时候,首先创建HandlerThread对象,并调用start方法启动该线程。
- 在HandlerThread的run方法中调用Looper.prepare()方法,为当前线程设置Looper和MessageQueue。并把Looper对象设置到成员变量
- 创建Handler对象进行发消息,传入Looper对象参数。由于HandlerThread的run方法需要在cpu分配给它时间片的时候才能被调用,所以Looper对象不会马上被创建,所以在getLooper对象的时候,首先判断Looper对象是不是空的,如果为空,则进行waite操作。当HandlerThread的run方法执行之后,初始化了Looper mlooper对象,然后对线程进行notify唤醒操作。getLooper继续执行,这样一来成员变量Looper mlooper就不为空。