Handler小结

首先有一个问题引出本篇文章:为什么更新UI线程的Handler必须在主线程中创建?
答:其实每个Handler都会关联一个消息队列,而消息队列封装在Looper中,而Looper又会关联一个线程(Looper通过ThreadLocal封装),最终每个消息队列会关联一个线程。Handler就是一个消息处理器,将消息投递给消息队列,然后再对应的线程中逐个取出消息,并且执行。因为取出消息后执行的动作在UI线程中。所以更新UI线程的Handler必须在主线程中创建。
下面从源码角度简单分析一下:
1.在Handler的构造函数中Looper的实例是通过myLooper()方法得到的:

 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()");
        }
      //...
    }

而myLooper()中Looper是通过sThreadLocal.get()获取的:

 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

那什么时候存储在ThreadLocal中的呢:

 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.prepare()方法中存储进去的。这也那个所有的就关联上了。
因为我们知道,在主线程中创建Handler不用prepare Looper,这事因为在主线程中系统默认创建了Looper,它在不停的独爱读分发消息,因此四大组件的调度、我们的输入事件、绘制请求才能得到处理。
ActivityThread 就是我们说的主线程,而它的 main() 方法就是当主线程的入口:

public static void main(String[] args) {
    /...
    Process.setArgV0("<pre-initialized>");

    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

}

所以我们可以推测出,Handler在子线程中创建Handler的时候必须手动加上prepare和loop方法,否则会抛出异常:Can’t create handler inside thread that has not called Looper.prepare()

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的判断,如果mLooper 为空,就会抛出异常。

//该写法会抛出异常!!!
new Thread(){
            @Override
            public void run() {
                handler = new Handler(new Handler.Callback() {
                    @Override
                    public boolean handleMessage(Message msg) {
                        Log.e("TAG",msg.obj.toString());
                        return false;
                    }
                });
            }
        }.start();

从上述程序中我们可以看到,当mLooper对象为空时,抛出了该异常,这是因为该线程的Looper对象还没有创建,因此sThreadLocal.get()会返回null。Handler的原理就是要与MessageQueue建立关联,并且将消息投递给MessageQueue,如果连MessageQueue都没有,那么Handler就没有存在的必要。而MessageQueue有封装在Looper中,因此创建Handler的同时必须保证Looper一定不为空。(因为sThreadLocal.get()为空即mLooper会为空,所以我们只需找到sThreadLocal.set()把mLooper穿进去,这样这里就不会为空了,就能正常运行了,而通过上述的源码发现在 Looper.prepare()的方法中sThreadLocal.set(new Looper(quitAllowed))赋值了),所以在子线程中必须手动加上Looper.prepare();

正确写法如下:

new Thread(){
            @Override
            public void run() {
                //1.为当前线程创建Looper,并且会绑定到ThreadLocal中去
                Looper.prepare();
                handler = new Handler(new Handler.Callback() {
                    @Override
                    public boolean handleMessage(Message msg) {
                        Log.e("TAG",msg.obj.toString());
                        return false;
                    }
                });
                //2.启动消息循环
                Looper.loop();
            }
        }.start();

我们加了两处:
第一是通过Looper.prepare()来创建Looper,第二是通过Looper.loop()来启动消息循环。这样该线程就有了自己的Looper,也就有了自己的消息队列,如果只创建Looper而不启动消息循环,虽然不会抛出异常,但是通过Handler来post或者sendMessage也不会有效,因为消息虽然被加到消息队列中了,但是并没有启动消息循环,也就不会从消息队列中获取消息并且执行了。

项目地址:https://github.com/fengxiaobing/MyHandler
注。由于是练习项目,该项目的代码中也有包含Thread和Runnable的优缺点的代码,不足之处还望多多指教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值