使用HandlerThread好处
在Android开发中,我们经常使用多线程编程。而多线程有多种方式实现。其中我们用得最多、也是最早接触的是使直接开一个线程Thread
。
new Thread(){
@Override
public void run() {
//do something here
}
}.start();
先抛开这样开线程的弊端,因为在有时做一些简单的任务时,这样写确实能减少代码量;我们先说说线程间通讯。Android自带提供Handler
、Looper
的组合来实现。使用方法相信大家都很熟悉了。而HandlerThread
就是为了实现多线程之间通讯的一个类,可以减少一定的代码量。
HandlerThread源码解析
首先,我们先看看源码(不感兴趣可以跳过,下方有使用方法解析):
package android.os;
/**
* 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.
*
* 这是一个方便启动一个带有`Looper`的`Thread`,然而`start()`还是需要被调用
*
*/
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*
* 输入的优先级是来自`android.os.Process`里的,而不是来自`java.lang.Thread`里的
*
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
这是HandlerThread
类的开头,我们可以看到,这个类属于android.os
包,属于sdk内的类,使用方便,无需另外导入。
该类继承于Thread类,本质上是一个线程。
该类提供两个构造方法,单参数构造方法,输入参数为线程名字,双参数构造方法输入线程名字以及线程优先级,这里需要注意一点,输入的优先级是android.os.Process
里的,而不是java.lang.Thread
里的。
我们再往下看代码:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
这是核心部分,当线程被启动后,调用该run()
方法。首先,记录了该线程的id号,然后调用Looper.prepare()
,创建该线程上的looper
对象。
接着对自身对象加锁,并获取该线程上的looper
引用,并调用notifyAll()
,唤醒其他正在等待该对象的线程。做这一步的原因,我们后面再做解释。
然后在进程里对当前线程设置优先级。
最后调用Looper.loop()
,启动looper
的消息队列循环。
到此,该类以及基本可以运行了
我们再往下看代码:
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
*
* 该方法会返回一个关联该线程的Looper
* 如果该线程还未启动,或者某种原因isAlive返回false,这个方法会会返回null值
* 如果线程已经启动,这个方法会阻塞,直到looper被初始化
*
* @return The looper.
*/
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
,首先判断该线程是否正在运行,若否,则返回一个空值(线程未启动,当然没有关联的looper
)。
接着对自身对象加锁,再次判断线程是否正在运行,且mLooper
是否已经赋值。这里用到的方法是双重检测,和单例模式中的双重检测是一样的。若线程已经在运行,但mLooper
未赋值,那就在该线程等待。
我们可以和上面的run
方法联系起来了。直到run()
方法执行到notifyALL()
,才被唤醒,继续判断,直到mLooper
赋值,返回mLooper
。
这样写的目的是为了防止多线程操作下,防止某个线程从一个已经运行的HandlerThread
但还未完成run
方法的对象里,获取到空的looper
引用。
HandlerThread用法
那么我们平时怎么使用HandlerThread
这个类呢?
首先我们可以创建一个HandlerThread
类实例,并运行该线程,那么该线程就开始进入等待消息的状态:
handlerThread = new HandlerThread("myHandlerThread");
handlerThread.start();
接着我们可以创建一个和该线程的Handler
,里面实现我们业务需求的执行的方法,构造方法里就可以传入我们HandlerThread
类实例的looper
,这样,handler
就和我们的线程关联在一起了:
handler =new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
//这里可以实现我们的业务需求
Log.i(TAG,"receive msg:"+msg.what);
}
};
接下来,就和我们平时使用handler
一样使用了,该任务就会在HandlerThread
实例的线程运行了:
handler.sendEmptyMessage(123);