- HandlerThread 类功能介绍
- 示例
- 源码浅析
- 总结
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类的线程类。那这个类有什么特点呢,注释中介绍到:
可以很方便的帮我们新建一个带有looper的线程。而且这个looper可以用来创建handler对象。但是start()方法还是必须要调用的。
写过线程间通信的朋友应该知道,线程的run方法中需要去处理一下looper的操作,而HandlerThread帮我们完成了这一步。
了解了这些就来使用吧
示例
- 创建一个继承自HandlerThread的类
public class MyHandlerThread extends HandlerThread {
private Handler mHandler;
public MyHandlerThread(String name) {
super(name);
}
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
if (mHandler == null) {
mHandler = new Handler(this.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i("name", "msg: ");
}
};
}
}
@Override
public void run() {
Log.i("name", "threadName1: " + Thread.currentThread().getName());
super.run();
}
@Override
public synchronized void start() {
super.start();
Log.i("thread", "start");
}
/**
* {@link #start} 之后调用
*
* @return
*/
public Handler getmHandler() {
return mHandler;
}
}
这里重写了onLooperPrepared方法,目的是looper开始轮询之前,实例化Handler对象,在示例化Handler的时候传入了当前线程的looper。所以Handler的handleMessage方法运行在子线程中。
- 接下来就很简单了创建HandlerThread并开启线程
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_thread);
ButterKnife.bind(this);
handlerThread = new MyHandlerThread("handlerThread");
handlerThread.start();
}
他毕竟还是一个线程类,不调用start方法线程是无法进入就绪状态的。
- 需要的地方写发送消息
@OnClick(R.id.send)
public void onViewClicked() {
Message obtain = Message.obtain();
handlerThread.getmHandler().sendMessage((obtain));
}
这三步就完成了UI线程向子线程中发送消息,如果子线程在执行完操作后返回给UI线程,只需要拿到一个UI线程的Handler即可。
- 子线程向UI线程发送消息
//实例化一个UIhandler
private Handler uiHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i("name", "msg: uiHandler");
}
};
...
@OnClick(R.id.send)
public void onViewClicked() {
Message obtain = Message.obtain();
//让消息携带UIhandler过去
obtain.obj=uiHandler;
handlerThread.getmHandler().sendMessage((obtain));
}
...
mHandler = new Handler(this.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i("name", "msg: MyHandlerThread");
if (msg.obj instanceof Handler){
Message obtain = Message.obtain();
//取出UIhandler,发送消息
((Handler) msg.obj).sendMessage(obtain);
}
}
};
上面四步,完成了UI线程和子线程的双向通信。
- 不需要的时候退出任务
比如退出界面的时候,需要退出HandlerThread。
HandlerThread有两个退出的方法
//清空队列中所有的消息
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
//清空队列中所有的延迟消息,派发完未发送非延迟的消息
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
//最终调用的是MessageQueue里的quit方法
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
//安全
removeAllFutureMessagesLocked();
} else {
//不安全
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
下面是copy:
从源码可以看出当我们调用quit方法时,其内部实际上是调用Looper的quit方法而最终执行的则是MessageQueue中的removeAllMessagesLocked方法(Handler消息机制知识点),该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。
当调用quitSafely方法时,其内部调用的是Looper的quitSafely方法而最终执行的是MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。最后需要注意的是Looper的quit方法是基于API 1,而Looper的quitSafely方法则是基于API 18的。
源码浅析
上面已经介绍了对出的两个方法。
- 构造方法
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.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
上面两个构造方法分别指定了线程名和优先级级别。继续点下去,回到Thread 的init方法,新建一个Thread对象
/**
* Initializes a Thread.
*
* @param g the Thread group
* @param target the object whose run() method gets called
* @param name the name of the new Thread
* @param stackSize the desired stack size for the new thread, or
* zero to indicate that this parameter is to be ignored.
*/
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
Thread parent = currentThread();
if (g == null) {
g = parent.getThreadGroup();
}
g.addUnstarted();
this.group = g;
this.target = target;
this.priority = parent.getPriority();
this.daemon = parent.isDaemon();
setName(name);
init2(parent);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
//实例化的时候就先让线程id+1
tid = nextThreadID();
}
...
private void init2(Thread parent) {
this.contextClassLoader = parent.getContextClassLoader();
this.inheritedAccessControlContext = AccessController.getContext();
if (parent.inheritableThreadLocals != null) {
//给当前线程创建ThreadLocalMap对象,并且继承parent的ThreadLocalMap中的数据
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(
parent.inheritableThreadLocals);
}
}
初始化一个线程的基本属性。有兴趣可以了解下类加载器ClassLoader和ThreadLocalMap。
- run方法
start之后接下来就是run方法了
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
run方法主要就是进行Looper的设置。HandlerThread另外提供了一个onLooperPrepared方法,方便我们在loop(),做一些准备工作。
/**
* @return a shared {@link Handler} associated with this thread
* @hide
*/
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
其实HandlerThread的内部有一个handler对象,不过对外设置了hide。
总结:
- 利用HandlerThread实现线程间通信,我们不需要再去关系Looper的一些调用和设置。
- 只需要处理好Handler对象的消息处理逻辑即可。
- 记得在适合的地方退出HandlerThread,避免内存泄露。