您的位置:首页 > 移动开发 > Android开发

从子线程不能直接新建一个Handler对象来剖析android的Handler机制

2015-03-30 00:06 344 查看


从子线程不能直接新建一个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));
}

发现调用这个
a0b8
方法即可新建一个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又是在什么地方初始化的呢?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐