Handler的工作原理

引入一个BUG

在介绍工作原理之前,先引入一个问题(BUG),为什么在子线程中执行 new Handler() 会抛出异常?

new Thread(new Runnable() {
	@Override
	public void run() {
		Handler handler = new Handler();
	}
}, "Thread#1").start();

崩溃如下

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:203)

出现BUG,最好的老师就是看源代码,因此跟踪源代码可以看到,在构造函数中

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

所以崩溃的原因是 如果当前线程没有Looper话,就会抛出如上的崩溃。
因此,我们的解决方法是

new Thread(new Runnable() {
	@Override
	public void run() {
		Looper.prepare();
		Handler handler = new Handler();
		Looper.loop();	
	}
}, "Thread#1").start();

这样创建的工作线程就具有了消息处理机制。FIX BUG,上线吧。

工作原理

虽然解决了BUG,但是我们还需要更一步的理解Handler的工作原理,上面的构造函数只是源代码的冰山一角。

Handler的主要工作包含消息的发送和接收处理两个过程。

消息的发送

一般我们会调用handler.sendMessage(msg),源代码是我们的好老师,进入源代码查看具体调用实现。

public final boolean sendMessage(Message msg) {
    return sendMessageDelayed(msg, 0);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

跟踪源代码的调用,Handler发送的消息最后是向消息队列MessageQueue插入了一条消息。

(跳跃一下)MessageQueue的next方法就会返回这条消息给LooperLooper收到消息后就开始处理。最终消息由Looper交给Handler处理,即Looper源代码中的msg.target.dispatchMessage(msg),这里的msg.target就是对应的的Handler

这也解释了一个问题(问题少年),如果有两个不同的Handler发消息,那么消息怎么知道是哪个Handler发送和处理的?就是通过上面的msg.target

这时就进入下个环节,Handler处理消息的阶段。

消息的处理

继续跟着源代码老师学习,消息的处理就需要看HandlerdispatchMessage方法实现。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

从上面可以看出,Handler处理消息的过程如下

1、检查Messagecallback是否为null,不为null,则直接调用HandlerhandleCallback处理。

这种场景对应的是用Handler.post(Runnable runnable)发送消息。处理消息的handleCallback实现如下:

private static void handleCallback(Message message) {
    message.callback.run();
}

message.callback就是Runnable实例。

2、检查mCallback是否为null,不为null则调用mCallbackhandleMessage处理。

这种场景对应的是初始化Handler时传入CallBack对象参数,即

public Handler(Callback callback) {
    this(callback, false);
}

这是另外一种使用Handler的方式,处理消息交给Callback的实现方法handleMessage

3、当上面两种场景不存在,或者存在场景2,但返回值为false(即需要进一步处理)时,就调用HandlerhandleMessage方法处理。

这种场景对应的是

Handler handler = new Handler() {
	@Override
	public void handleMessage(Message msg) {
		...
	}
};

handler.sendMessage(msg);

总结归纳也是学习的好老师,把Hander处理消息的过程总结成一个流程图,如下:

Handler消息处理流程图.png

扩展问题

好奇是学习的另一位良师,那么问题来了。

问题1:接着开始的BUG,为什么我们在Activity初始化Handler时,没有写Looper.prepare,但不会崩溃?

这是因为Activity所在的UI线程(主线程)在初始化时,创建了一个消息队列和消息循环,具体可以看源代码的ActivityThread类的main方法。

public static void main(String[] args) {     
    ...
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();

    Looper.loop();
    ...
}

问题2:如何直接获取UI线程(主线程)的Handler?

解决方式如下

Hander mHandler = new Handler(Looper.getMainLooper());

传入主线程的Looper, 初始化Handler一步到位。

参考

任玉刚 《Android开发艺术探索》

Handler的使用 by自己

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值