《Android开发艺术》读书笔记-消息机制

之前写了一篇文章Android Handler总结 ,介绍了Handler的基本用法和基本原理。
本文是读完Android的消息机制的读书笔记, 从源码分析了消息传递的机制, 对于Handler,以及Looper和MessageQueue有了更深刻的理解。

消息机制的概述

主要申明几个概念,也是接上一篇Android Handler总结 中的原理的内容,再阐述几个重要的概念:

  • Handler, 作为Android消息机制的上层接口, 可以轻松地将一个任务切换到其所在的线程去执行。
  • 更新UI, Handler的存在并不是仅仅为了更新UI, 它的使用场景:当我们在子线程中执行耗时的I/O操作(读取文件/访问网络等), 当这些操作完成后需要更新UI界面,而子线程并不能直接更新UI,这个时候就可以通过Handler将更新UI操作切换到主线程。
  • MessageQueue, 消息队列, 提供了消息的插入和删除操作,但是内部的实现是采用单链表的形式。
  • Looper, MessageQueue存储消息,Looper则是处理消息;
  • ThreadLocal, 作为Looper内部重要的概念, ThreadLocal可以在不同线程中互不干扰地提供数据的存储。(后面会重点提到)

ThreadLocal 工作原理

ThreadLocal 是一个线程内部的数据存储类, 通过它可以在指定的线程中存储数据,并且在指定线程中才可以获得存储的数据, 其他线程则不能。
基于这个功能, 在某些特殊的场景下,用ThreadLocal可以实现一些看起来很复杂的功能,源码中Looper, ActivityThread, 以及AMS都用到了ThreadLocal。
下面是两个应用场景:

  • Looper
    以Looper为例: 对于Handler来说, 它需要获取当前的线程的Looper才能继续工作, 很显然,Looper的作用域是线程,并且不同线程有不同的Looper, 这个时候,ThreadLocal就可以轻松地实现Looper的存储功能。
    相反,如果不使用ThreadLocal存储,那么就必须提供一个全局的hash表供Handler存储和查询相应的Looper,同时需要一个LooperManager管理,会使逻辑更加复杂。
  • 监听器
    书本中还举了另外一个应用场景,复杂逻辑下的对象传递,例如贯穿整个线程执行的监听器。如果正常的传递,那么有两种方法:
    1, 通过函数参数传递,但是如果函数调用栈很深,逻辑很复杂的时候,这样的设计会很糟糕;
    2, 通过一个全局的静态变量实现,但是在多线程并发的时候, 提供多个静态的监听器对象也是非常糟糕的。

工作过程
ThreadLocal之所以能在不同的线程中保存不同的数据, 是因为ThreadLocal会从鸽子的线程中取出一个数组, 然后从数组中根据当前的索引去查找出对应的值。

内部实现
ThreadLocal是一个泛型类,我们主要看它的get和set方法:

public void set(T value){
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if(values == null){
        values = initializeValues(currentThread);
    }
    values.put(this, value);
}

ThreadLocal中有一个成员变量values存储线程的数据,values内部有一个数组:Object[] table, ThreadLocal的值就存在着歌table数组中。
它的存储规则: table数组中的存储位置总是为Thread Local的reference字段所标识对象的下一个位置。
所以,ThreadLocal的get方法就是得到reference标识的后一个对象的值, 如果为空,则返回初始值。

MessageQueue 工作原理

MessageQueue,消息队列,但是其内部实现并不是用队列,而是单链表的数据结构,因为单链表在插入和删除上比较有优势。
他的两个核心方法是: enqueueMessage 和 next
enqueueMessage主要是单链表的插入操作
next是一个无限循环的方法, 如果消息队列中没有消息,那么next会阻塞在这里, 当有新的消息过来的时候, next会返回这条消息并将其踪单链表中删除。

Looper 工作原理

Looper扮演着消息循环的角色, 会不停地从MessageQueue中查看是否有新的消息, 若有,则处理, 否则,会一直循环阻塞。
Handler的工作需要Looper, 通过Looper.prepare()即可为当前线程创建Looper。
Looper可以通过quit()方法退出, Looper退出以后, 这个线程会立刻终止, 如果Looper不退出, 线程则会一直处于等待状态(建议处理完后退出Looper)
Looper只有调用了loop()方法后, 消息循环系统才会真正开始工作
loop()方法的部分代码如下:

public static void loop(){
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;

    for(;;){
        Message msg = queue.next();
        if(msg = null){
            return;
        }
        msg.target.dispatchMessage(msg);

        msg.recycleUnchecked();
    }
}

上述代码中,如果MessageQueue返回了新消息, Looper就会处理消息: msg.target.dispatchMessage(msg), 其中msg.target是发送消息的handler对象, 这样,这条消息最终又交给handler的dispatchMessage来处理了。
但是, 这里巧妙的就是, dispatchMessage方法是在创建Handler的线程中之行的, 这里通常就会是一些更新UI的操作了。

Handler 工作原理

Handler的工作主要包含消息的发送和接收过程。
消息发送通过post和send的一系列方法实现,就是将消息插入消息队列MessageQueue。
然后Looper就会处理消息,最终又交给Handler的dispatchMessage方法处理, 下面看看dispatchMessage方法的代码实现:

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

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

首先,msg中如果存在callback, 就执行callback的run方法, 可以看出callback就是一个runnable对象, 即Handler的post方法传递的参数。
其次,再检查mCallback对象, mCallback对象是通过构造handler的时候传递的参数。
在日常开发中, 创建handler的最常见方式就是派生一个handler的子类, 并重写其handleMessage方法; 但是我们也可以通过直接new一个Handler对象,在参数中实现一个callback重写handleMessage方法。

主线程消息循环

最后一节内容提到了Android的主线程,即UI线程ActivityThread, 主线程通过Looper.prepareMainLooper()来创建Looper以及MessageQueue, 并通过Looper.looper()开启消息循环。
然后,ActivityThread创建了一个Handler: ActivityThread.H, 它的内部定义并处理了四大组件的启动和停止消息。
ActivityThread和AMS(Android Manager Service)进程间通信是通过ApplicationThread完成的, 首先AMS会毁掉ApplicationThread的Binder方法, 然后ApplicationThread像ActivityThread.H发送消息, H收到消息后, 切换到主线程区执行代码逻辑(例如我们在Activity中重写的onStart()等生命周期相关方法)。
整个就是主线程的消息循环模型。

你可能感兴趣的:(《Android开发艺术》读书笔记-消息机制)