在对Android异步消息处理机制源码剖析和Android AsyncTask源码剖析后,下面对Android中的另一个和异步消息机制相关的类-HandlerThread进行全面解析。
为什么要有HandlerThread?
我们已经知道,主线程与子线程之间的通信,可以依靠Handler,Looper和MessageQueue来实现。一个线程中可以有唯一的Looper对象,负责消息循环;一个Looper中都有唯一的MessageQueue,管理者消息队列;Handler在发消息前,必须与一个Looper进行绑定,也就与MessageQueue进行了绑定。一切准备就绪后,可以通过Handler把消息Message发送出去,Message会在MessageQueue中入队,然后等待Looper把消息取出并交给发送消息体的Handler进行消息的处理。
如果是主线程向子线程进行通信,在创建Handler之前,则必须在子线程中调用Looper.prepare()方法为线程初始化一个Looper对象。一旦忘记调用Looper.prepare()方法,在创建Handler的时候会抛出异常。
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
...
}
所以Google为了避免开发者忘记调用Looper.prepare()而导致的异常,对Thread类进行了封装,使得Thread类在初始化的时候便有了一个Looper,这便是我理解的HandlerThread产生的由来。如有不对的地方,欢迎指正交流。
HandlerThread的源码剖析
HandlerThread源码非常的少,只是对Thread进行了简单的封装,如果对Android异步消息机制了解的同学,理解它的源码非常的简单。下面来看HandlerThread的源码。
public class HandlerThread extends Thread {
int mPriority; //线程执行优先级
int mTid = -1; //调用线程的标识符
Looper mLooper; //线程内部的Looper对象,一个线程只有一个Looper对象
private @Nullable
Handler mHandler; //与Looper绑定的Handler对象
public HandlerThread(String name) {
super(name);
//设置优先级
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
//Looper开启循环前调用,子类可按需覆写
protected void onLooperPrepared() {
}
@Override
public void run() {
//返回调用线程的标识符
mTid = Process.myTid();
//初始化线程本地变量Looper
Looper.prepare();
synchronized (this) {
//从Looper中拿到本线程的Looper对象
mLooper = Looper.myLooper();
notifyAll(); //唤醒线程
}
Process.setThreadPriority(mPriority);
onLooperPrepared(); //回调方法,在loop之前,子类可按需覆写实现
Looper.loop();
mTid = -1;
}
//返回looper对象
public Looper getLooper() {
//如果线程不是可用状态,则返回null
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
//如果线程被start了,但是mLooper还没有初始化,则让线程进入等待状态,直到mLooper对象被初始化后线程被唤醒
synchronized (this) {
while (isAlive() && (mLooper == null)) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
@NonNull
//懒加载初始化Handler的单例对象,该handler与此线程获取的Looper进行绑定
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
//退出,执行的是looper.quit,其实执行的是Looper中MessageQueue.quit方法
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
//同上述方法,执行的是MessageQueue.quit方法
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
//返回调用线程的标识
public int getThreadId() {
return mTid;
}
}
以上便是HandlerThread的全部源码,非常的简短,下面做以下几点说明:
- HandlerThread继承自Thread,所以它的本质还是线程的一种实现;
- HandlerThread内部已经有了一个Looper对象,在线程启动的时候,会初始化线程本地变量Looper;
- HandlerThread内部有Handler对象,但采用的是懒加载的单例,在使用之前并未进行初始化;Handler对象绑定的Looper就是本线程的Looper对象;
- 在调用getThreadHandler()方法和getLooper()方法之前,必去确保线程已经启动了(调用了start()方法),否则线程会进入等待状态,直到在run()方法内部mLooper对象被赋值;
- 在Looper开启循环(调用loop()方法)之前,会执行onLooperPrepared()方法,它默认是一个空实现,可在子类中按需覆写实现,注意,它的执行逻辑是在子线程,所以不要进行UI操作;
- quit方法和quitSafely方法调用的是Looper对象的相应方法,其实最终调用的都是Looper内部MessageQueue的quit方法,这个在Android异步消息处理机制源码剖析分析的已经很清楚了,不明白的同学可以去了解下;
- HandlerThread只是把线程和Looper进行了简单的封装,剩下的很多操作跟异步消息处理机制是一样的;
HandlerThread使用场景
在Android异步消息处理机制源码剖析文章中,主线程向子线程进行通信,给了一个case:
//创建子线程
class WorkThread extends Thread {
private Looper looper; //取出该子线程的Looper
public void run() {
Looper.prepare(); //创建该子线程的Looper
looper = Looper.myLooper(); //取出该子线程的Looper
Looper.loop(); //只要调用了该方法才能不断循环取出消息
}
}
private Handler mHandler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
thread = new WorkThread();
thread.start();
//创建Handler时把looper做入参,把Handler与Looper绑定在一起
mHandler = new Handler(thread.looper) {
public void handleMessage(android.os.Message msg) {
//在子线程中处理消息的逻辑
};
};
mHandler.senMessage(Message.obtain());
}
上述例子很简单,它其实跟HandlerThread的实现思路是一致的,所以可以改成以下方式:
private private Handler mHandler;
class WorkThread extends HandlerThread {
@Override
public void run() {
super.run();
//执行业务操作
}
@Override
protected void onLooperPrepared() {
//按需选择是否覆写,在loop之前想做的操作
}
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
HandlerThread thread = new WorkThread();
thread.start();
//通过ThreadHander去获取Looper来创建Handler
mHandler = new Handler(thread.getLooper()) {
public void handleMessage(android.os.Message msg) {
//在子线程中处理消息的逻辑
};
};
mHandler.senMessage(Message.obtain());
另外,如果不需要Handler对消息进行处理,也可以调用以下方式直接获取HandlerThread中的Handler发送消息:
HandlerThread thread = new WorkThread();
thread.start();
//通过ThreadHander去获取Looper来创建Handler
mHandler = thread.getThreadHandler();
mHandler.senMessage(Message.obtain());
在Android异步消息处理机制源码剖析中分析知道,在Looper中获取消息后,调用Handler.diapatchMessage()方法进行处理,处理的优先级顺序依次是:Message的callback(Runnable对象) -> Handler的Callback(实现了handleMessage方法的Callback接口实现类对象) -> Handler自身的handleMessage()方法。所以如果用HanderThread中的Handler想要处理Message的话,可以用以下两种方式:
- 一种是给Handler设置Callback对象,需要实现Callback接口,handlerMessage()方法的返回值表示是否要消耗掉这个Message,不再调用Handler自己的handleMessage()方法,由于HandlerThread内部的Handler并没有覆写这个空方法,所以这里返回值true和false都可以:
HandlerThread thread = new WorkThread();
thread.start();
//通过ThreadHander去获取Looper来创建Handler
mHandler = thread.getThreadHandler();
mHandler.setCallback(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg){
//处理消息的逻辑
}
});
mHandler.senMessage(Message.obtain());
- 另一种方式,便是在获取一个Message对象的时候,给它设置callback:
HandlerThread thread = new WorkThread();
thread.start();
//通过ThreadHander去获取Looper来创建Handler
mHandler = thread.getThreadHandler();
final Message msg = Message.obtain(mHandler, new Runnable(){
@Override
public void run() {
//执行消息的处理,可以通过msg去获取传递的值进行逻辑处理
}
});
mHandler.senMessage();
参考链接
- Android异步消息处理机制源码剖析
- Android AsyncTask源码剖析
- Java中的线程Thread总结