Android Handler消息机制的理解

最近在看《深入理解Android内核设计思想》,看到有关Handler消息机制这部分,以前一直对这块似懂非懂,其实说白了就是还不懂,现在看过这篇后可以说是受益颇多,作者从源码的角度深层次的解析加上形象生动的语言描述,可谓良书一本,下面就针对自己对整个消息机制的理解做个总结。

一、前言

我们都知道,Android在子线程中直接更新UI操作时,会报出异常

android.view.ViewRoot$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.


意思就是说,只有原始的线程(主线程/ui线程)才能修改view对象。在子线程中修改view的显示状态,会报上面的异常

这是为什么呢?

因为在Android系统中UI主线程要负责执行UI的渲染、View的绘制,这些操作都需要非常高的时效性,以保证界面不会卡顿,甚至无响应(ANR),而子线程有可能需要执行较长时间的耗时操作,比如连接网络获取数据、读取数据库获取数据等,如果把UI操作都放在子线程,则UI操作就必须等待耗时操作执行完才能绘制出来,这样便很容易引起界面无响应。

所以Android便引入了消息机制,来实现子线程和主线程之间的通信传递数据。

简单来总结一下就是:子线程获取到数据之后,不直接进行UI更新,而是把数据’装’到消息中发送到主线程,主线程中有一个循环轮询会立即收到子线程发送过来的信息,然后拿到消息数据后在主线程更新UI。

做法:通常是在主线程new一个handler,然后子线程通过handler来发送消息。最终是在handler的handleMessage方法中处理子线程发送过来的数据消息,直接进行UI更新。

那么消息机制到底是怎样运作的呢?下面开始一步步探讨

二、Handler, MessageQueue, 与 Looper之间的关系图


先来简单认识一下它们之间的关系和作用:

  • Handler 消息处理者
    它主要有两大作用:① 处理Message。② 发送Message,并将某个Message压入到MessageQueue中。

  • Looper 轮询器
    在 Looper里面的 loop()函数中有个死循环,它不断地从 MessageQueue 中取出一个Message,然后传给Handler进行处理,如此循环往复。假如队列为空,那么它会进入休眠。

  • MessageQueue 消息队列
    这个集合里面装有很多个Runnable、Message。

三、Handler 是什么?

从字面上理解,Handler的意思是:“处理者”,那么Handler处理的是什么呢?下面我们开始一步步探讨

在Android代码中,当我们在创建Handler实例,则会报如下错误:

意思就是说:在没有调用Looper.prepare()之前不能在子线程创建Handler(那么为什么不能这样?看完解析第二步马上会有答案)

先来解释一下,为什么在主线程中我们就已经可以直接创建Handler?

因为在Activity的启动代码中,已经在当前UI线程(主线程)调用了Looper.prepareMainLooper()和Looper.loop()方法。我们可以在源码的ActivityThread类中看到,如图:

从上图可以看出,在主线程中首先调用的是Looper.prepareMainLooper(),然后创建了一个ActivityThread实例,最后通过Looper类使主线程进入消息循环中。

在子线程创建handler的步骤
  • 1.Looper.prepare();
  • 2.实例化Handler,在构造方法中实现handlerMessage(Message msg)处理消息
  • 3.Looper.loop();
    示例:

    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.prepare(),源码做了哪些操作

public class Looper {  
    ......  

    private static final ThreadLocal sThreadLocal = new ThreadLocal();  

    final MessageQueue mQueue;  

    ......   
    public static final void prepare() {  
        if (sThreadLocal.get() != null) {  
            throw new RuntimeException("Only one Looper may be created per thread");  
        }  
        sThreadLocal.set(new Looper());  
    }   

    public static final void prepareMainLooper() {  
        prepare();  
        setMainLooper(myLooper());  
        if (Process.supportsProcesses()) {  
            myLooper().mQueue.mQuitAllowed = false;  
        }  
    }  

    private synchronized static void setMainLooper(Looper looper) {  
        mMainLooper = looper;  
    }  


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

    private Looper() {  
        mQueue = new MessageQueue();  
        mRun = true;  
        mThread = Thread.currentThread();  
    }  

    ......  
}  

在调用 Looper.prepare() 方法时,创建一个 Looper 对象,这个对象是被 set 绑定到一个ThreadLocal(线程局部变量)中

sThreadLocal.set(new Looper());
  ThreadLocal的作用就是保证每一个调用了prepare()函数的线程里面都有一个唯一的Looper对象。
意思就是说:如果在一个子线程中创建一个 Handler,那么它首先调用 Looper.perpare()方法时,创建的 Looper 对象是新的,与主线程不同。

new Looper();
  在创建一个 Looper 对象时,同时 new 了一个MessageQueue消息队列,后续消息就是存放在这个队列中去的,这会就可以很明白知晓最开始的那张关系图了,即 Looper 中包含了一个 MessageQueue【这句话非常重要】。

第二步 我们在子线程中创建 Handler,Handler 到底是如何与 Looper 关联起来的呢?

public Handler mHandler;
...
mHandler = new Handler() {
       public void handleMessage(Message msg) {
            ...
       } 
};

我们在 new Handler()时,Handler 的构造函数有如下几种:

public Handler();
public Handler(Callback callback);
public Handler(Looper looper);
public Handler(Looper looper, Callback callback);

之所以有这么多构造函数,是因为 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()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
  }

从上面代码可以看出,创建一个 Handler 时,需要获取 Looper 轮询器和 MessageQueue 消息队列,它们都作为 Handler 的成员变量,这样 Handler 和 Looper,MessageQueue 就联系起来了。

现在终于可以解释为什么在没有调用Looper.prepare()之前不能在子线程创建Handler:
因为在new Handler 的时候,首先先要有 Looper 轮询器对象(在Handler底层中是通过Looper.myLooper()获取),然后在 new Looper 轮询器的同时new MessageQueue,然后Handler在底层中再通过Looper对象的成员属性mQueue获取到MessageQueue。这样在 Handler 的构造函数中才能获得 looper和 messageQueue

第三步 Looper.loop() 轮询器是怎么轮询的

public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    ...
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            return;
        }
        ...
        msg.target.dispatchMessage(msg);
        ...
    }
}

上面代码比较多,我们就看一句关键的代码 msg.target.dispatchMessage(msg);

从上面代码也可以看出,轮询器在for (;;){}死循环代码块中不断的执行, 通过 queue.next();从 MessageQueue 中取出一个 Message,当msg不为空时,执行msg.target.dispatchMessage(msg);(实际上最终是调用到了Handler的dispatchMessage方法去拦截消息

在Message 中的源码中,target 就是当前线程的 Handler对象,msg的成员变量target是在发送消息的时候设置好的,一般就通过哪个Handler来发送消息,就通过哪个Handler来处理消息。

接下来我们来看 Handler 中的dispatchMessage(msg)方法

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

继续调用 handleMessage(msg);

/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}

意思就是说:每次我们 new Handler()的时候必须实现handleMessage(msg)方法才能接收到message。

现在我们可以得出一个结论:那就是 Handler 最终是通过handleMessage(msg)来处理消息

那么 Message 消息是由谁发送的呢?

我们一般在子线程中通过handler.sendMeaaage(msg)来发送消息,由于在子线程中禁止更新 UI,所以我们可以通过子线程中处理获得的数据,通过 handler 发送出去,这样数据便传递到了主线程,最终回到 handleMessage(msg)这个方法中对消息进行处理。

我们来看 Handler 的源码:
handler.sendMessage(msg)最终调用的是sendMessageAtTime

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);
}

从上面的代码可以看出:handler最终是把 msg 压到了 MessageQueue 中。

这个时候在MessageQueue消息队列中,就有了 Message 消息,由于 Looper 是死循环的执行 loop()方法不断的轮询从MessageQueue中取出 message,最终又把 message 传递到了在主线程的 handler 让他去执行handleMessage(msg)来处理一开始从子线程发过来的 message。

结论:Handler 还负责将某个消息压入 MessageQueue 中(发消息)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值