上一篇文章我们从源代码的角度分析了Handler的实现,这篇文章我们说下如何在子线程中创建Handler,在子线程中创建Handler只需2步:
为了创建当前Thread的Handler对外提供一个方法获取当前的Looper,
Java代码如下:
public class HandlerThread extends Thread {
private Looper mLooper;
@Override
public void run() {
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
}
Looper.loop();
}
public Looper getLooper() throws Exception {
if (!isAlive()) {
throw new Exception("current thread is not alive");
}
if (mLooper == null) {
throw new Exception("current thread is not start");
}
return mLooper;
}
}
下面我们来使用下看看:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val handlerThread = com.example.handlerdemo.HandlerThread()
handlerThread.start()
val handler1 = object : Handler(handlerThread.getLooper()) {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
Log.d("aaa", "thread:${Thread.currentThread().name},handle message")
}
}
handler1.sendMessage(Message.obtain())
}
}
上面的代码非常简单就是创建HandlerThread并start,然后通过HandlerThread的looper创建Handler,发送一条消息进行验证。
然而我们运行我们会发现直接crash,异常如下:
找到对应的代码我们发现是mLooper未初始化造成的,可我们的代码是先start然后获取的looper,为什么会没有初始化呢?其实这里就是典型的线程同步问题,由于我们启动HandlerThread和创建Handler都是在主线程中运行的,而HandlerThread的run方法是运行在子线程中,所有导致获取looper时HandlerThread还未初始化looper,那么如何解决这个问题呢?
最简单的解决方案:通过notifyAll和wait
public class HandlerThread extends Thread {
private Looper mLooper;
@Override
public void run() {
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Looper.loop();
}
public Looper getLooper() throws Exception {
if (!isAlive()) {
throw new Exception("current thread is not alive");
}
synchronized (this){
if (null == mLooper){
wait();
}
}
return mLooper;
}
}
在运行,输出日志如下:
我们发现handler处理是在子线程中处理而非主线程。
其实解决线程同步有很多办法,比如无限循环等待、Semaphore(信号量)等,不过建议使用上面的方式。
其实SDK已经为我们提供了HandlerThread,我们无需自己来实现,有人可能就说了你这不是蛋疼吗?
我想说我们不仅仅要会使用SDK提供的api,我们还要学会其中的思想,谁敢说我清楚所有SDK的类和api呢?在开始的时候我真不知道有HandlerThread这个类,然后项目上有需求才实现了这个类,当时我记得很清楚,开始没有注意线程同步的问题,使用的时候才发现的,这个类虽然非常简单,但涉及到非常多的知识,比如Handler的运行原理、线程同步等。
下面我们看看系统的HandlerThread:
通过这2个构造函数我们发现可以对此线程设置名称和优先级。
与我们的run方法对比多了onLooperPrepared方法,这个方法是让子类重写的。
我们在看下getLooper方法:
和我自己写的区别主要在synchronized代码块内,区别如下:
- 我们是if判断,而系统是while循环判断。
- 我们没有在次判断isAlive,而系统判断了。
- 未抛出异常,而是直接返回了null。
我们先想第一个问题是否需要while循环,想想如果用户调用了
handlerThread.notifyAll();
那我们将会返回null,显然这不是我们想要的,所以while循环是很必要的,只要looper为null将一直等待。
第二个问题:上面已经判断了isAlive(),while循环是否需要再次判读,注意wait是挂起操作,如果在挂起的时候用户结束了线程,那么此时线程已经结束了,所以需要判断。
第三个问题:是抛出异常还是返回null,现在想来个人觉得这2种方法都不是很好,首先返回null,这需要用户处理null的情况,抛出异常在使用上会不太方便,用户需要自己处理异常,使用如下:
需要用户自己try…catch
不过2选1的话个人认为返回null更好些,不过需要我们使用的时候注意一些。
继续看HandlerThread源码:
这里提供了一个Handler对象,如果使用了这个Handler,那如何重写handleMessage方法呢?暂时没有想到这个方法的意义在哪?
继续看:
这里提供了2个退出的方法,这2个方法是looper的方法,这里不再解释了。
结语:就是这么一个简简单单的功能,可里面涉及到很多方面,在看源码的时候更重要的是学会他们的思想,在看源码的时候可以想想如果是我来写这个功能,我如何来写?千万不要只是想想,不实际动手写,写完后在和系统的对比才会学到更多的东西。