从子线程不能直接新建一个Handler对象来剖析android的Handler机制
前言
错误案例:在子线程中新建Handler报错:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
代码案例:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(){
@Override
public void run() {
new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
}
}.start();
}
一、异常提示定位
首先我们到Handler类中去寻找错误提示的语句,在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()");
}
发现之所以不能再子线程中新建Handler对象的原因是用为Looper对象为null
再点开Looper.myLooper();的源代码:
发现
public static Looper myLooper() {
return sThreadLocal.get();
}
Looper对象是存放在
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
中这个对象是以线程的ID为键值,所以get方法取出的都是当前线程对应的Looper对象
所以之所以不能再子线程中定义Handler对象的原因就是用为我们没有主动的新建一个Looper对象并放到sThreadLocal 中
进一步思考
如何在子线程中新建一个Looper对象?我们去查看Looper的public static void prepare()方法:
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
发现调用这个方法即可新建一个Looper对象并放入该线程的sThreadLocal中
而sThreadLocal,是一个静态变量。在Looper.class被加载进内存的时候就已经初始化了,所以要想在子线程中新建一个Handler对象,可以用一下写法。
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
当我们调用Looper.looper()方法开始,Looper就会不断了去轮询MessageQueue.这是一个先入先出的栈结构。
public static void loop() {
……
for (;;) {
//此处如果queue没有更新的msg了就会阻塞线程
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
……
//执行msg
msg.target.dispatchMessage(msg);
……
//msg重置
msg.recycleUnchecked();
}
}
注释表明当消息池为空的时候,轮询器会阻塞在那里,不会有额外的性能支出。在此还提出几个问题待以后再来讨论。
问题一:轮询器被阻塞后又是谁来负责唤醒呢?
问题二:轮询器所在的线程是哪个线程,这个线程有什么特点,主要用来干什么?
问题三:为什么在主线程就可以直接新建Handler,主线程的Looper又是在什么地方初始化的呢?