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

从子线程不能直接新建一个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又是在什么地方初始化的呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值