Android源码学习笔记1_消息机制Handler

Android源码学习笔记1_消息机制Handler

由于以后以后要从事安卓系统方面的工作,然而本人半个月前还是Android小白. 这几天学习了gityuan的Android操作系统架构, 按照自己喜欢的方式总结一下. 有兴趣的同学可以直接看原博客.
地址:http://gityuan.com/2016/01/01/handler-message-usage/.

Android消息机制的四个类都是在framework/base/core/java/andorid/os/包下.

framework/base/core/java/andorid/os/
  - Handler.java
  - Looper.java
  - Message.java
  - MessageQueue.java

1.事先准备

我准备以gityuan博客上的例子, 以一条线的形式, 把消息传递的工作给描述出来.
首先, 先了解一下这四个类之间的关系.

1.1 Looper类中有以下重要成员变量和方法申明

(这里想说明一点的是, gityuan的博客中介绍Looper时候有个post()方法, 但是该post()方法的实现和Handler中post()方法是一样的, 并且我自己看源码之后发现Looper类中并没有post()方法)

//这个Looper的引用,用myLooper()方法后指向存储在当前线程本地栈中的Looper
private static Looper sMainLooper;
//ThreadLocal类型的变量, 内部只有一个存储在当前本地栈中的Looper副本
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//每一个Looper还拥有一个MessqgeQueue实例的引用
final MessageQueue mQueue;
//Looper类中保存当前线程的Thread, 在构造器中通过Thread.currentThread获得.
final Thread mThread;
//重要程度递减
public static void loop();
public static void prepare();
public static @Nullable Looper myLooper();
public void quit();

1.2 Handler类中的主要成员变量和方法申明

//Handler在构造器中, 通过myLooper()方法获得Looper, 然后通过Looper.mQueue获得MessageQueue实例的引用
final Looper mLooper;
final MessageQueue mQueue;
//用于消息分发
public void dispatchMessage(Message msg);
//这类方法有很多,他们互相调用,主要是用来队列排列用的.维护Message在队列中的顺序, 最终都是调用Handler类的MessageQueue.enqueueMessage()方法来入队
public final boolean sendMessage(@NonNull Message msg);
//传一个Runable接口类型的实例进去,最后还是调用Handler类的MessageQueue.enqueueMessage()方法来入队, 待会解释
public final boolean post(@NonNull Runnable r);
//Handler类自己的入队方法, 内部调用MessageQueue.enqueueMessage()方法真正入队
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

1.3MessageQueue类

MessageQueue类是一个消息队列, 它的作用就是用来维护消息传递 入队,出队.
它是消息机制的Java层和C++层的连接纽带,大部分核心方法都交给native层来处理. 暂时只要知道它几个方法的作用就行, 不影响本文阅读, 具体想要了解的可以在之后的博客结合native层进行分析.

//构造器
MessageQueue(boolean quitAllowed);
//提取下一条message
Message next();
//添加一条message到队列
boolean enqueueMessage(Message msg, long when);
//从消息队列头部开始中移除message
void removeMessages(Handler h, int what, Object object);
//每一个普通Message必须有一个target,对于特殊的message是没有target,即同步barrier token。 
//这个消息的价值就是用于拦截同步消息,所以并不会唤醒Looper.postSyncBarrier只对同步消息产生影响
//对于异步消息没有任何差别。
public int postSyncBarrier()

1.4Message类的主要变量和方法申明

Message中有一个变量池,.
关于该变量池的作用, 如果学过Javaweb可以参考数据库连接池,和Hibernate一级缓存的作用.
也可以看下面obtain()函数的英文注释

//每个Message都有一个Handler类型的target变量, 表示该Message要被哪个Handler处理.
Handler target;  
//Message中的callBack, 可以在Handler.post(Runnable callback)中指定该callback/回调方法
Runnable callback;
//next是一个Message类型的变量, 该变量指向下一个消息池中的Message
//也就是说Message使用类似链表的结构维护了一个消息池
Message next;
//Meaaage类内部有一个消息池, 用一些静态成员构成.
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;
//下面两个方法用来维护消息池中Message的还回消息和提取消息
/**
* Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
 //内部实现就是, 从消息池链表的表头取走Message,然后断开链表 
public static Message obtain();

/**
     * Return a Message instance to the global pool.
     * <p>
     * You MUST NOT touch the Message after calling this function because it has
     * effectively been freed.  It is an error to recycle a message that is currently
     * enqueued or that is in the process of being delivered to a Handler.
     * </p>
     */
//把不再使用的消息加入消息池, 将Message加入到消息池的过程,都是把Message加到链表的表头;
public void recycle()

2.线性探究, 解析消息机制的源码过程

还是用gityuan博客的例子.

代码:

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare(); //Looper初始化  

        mHandler = new Handler() {  
            public void handleMessage(Message msg) {
                //TODO 定义消息处理逻辑. 
            }
        };

        Looper.loop();  //开启无限循环的消息分发, 当MessageQueue中没有消息的时候跳出循环
    }
}

2.1 我们从Looper.prepare()进入

1)当Looper.prepare()执行之后, 其内部默认调用prepare(true)的重载, true表示当前Looper允许退出,
2)然后看ThreadLocal类型的sThreadLocal变量内部是否保存了一个Looper实例.
3)如果还没有, 则实例化一个Looper放在sThreadLocal中.

public static void prepare() {
        prepare(true);
    }

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类中,
我们来看看Looper的这个构造函数Looper(quitAllowed),

4)Looper构造函数中, 先是实例化了一个MessageQueue给自己的mQueue变量,
5)然后允许Looper.prepare的是一个线程吧, 这个线程又运行到Looper构造方法了, 那么Looper的构造方法就把当前的Thread搞到自己mThread中.

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

2.2 看看new Handler()干了啥

现在已经在Handler类了,
1)new Handler()执行之后, 内部调用Handler类自己的另一个构造方法重载.
2)Hander构造的时候, 从当前线程的私有栈中把Looper对象给得到了, 给了mLooper变量
3)我们通过上面的了解已经知道Looper自己是有一个MessageQueue对象的, 变量名为mQueue.
把这个mQueue也给Handler自己的mQueue传递了一个引用.(两个引用指向同一个MessageQueue对象)
4)当然, 由于这里是调用的无参Handler(), 所以callback为null, async为false.

public Handler() {
        this(null, false);
}
public Handler(@Nullable Callback callback, boolean async) {
        .......
        
        //必须先执行Looper.prepare(),才能获取Looper对象,否则为null.
        mLooper = Looper.myLooper(); //从当前线程的TLS中获取Looper对象
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
       mQueue = mLooper.mQueue; //消息队列,来自Looper对象
       mCallback = callback;  //回调方法
       mAsynchronous = async; //设置消息是否为异步处理方式
    }
    

2.3 最重要的Loop.loop()

1)Looper.loop()运行之后,首先回去调用该函数线程私有存储的Looper,
2)如果从当前线程获取不到Looper实例, 就抛异常.
3)获取到了就通过该Looper实例me, 获取MessageQueue.
4)之后进入无限循环,从MessageQueue中一直获取下一个Message,如果Message为空loop()方法就会return,相应的无限循环就会结束.
如果Message不为空, 该Message通过自身target指向的Handler进行消息分发
5)消息完成之后, 把Message放入消息池, 消息池就是上面介绍Message类时说的消息链表.

public static void loop() {
        //获取Looper对象
        final Looper me = myLooper();       
        //检查当前线程的私有栈是否已经有Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //从Looper中获取MessageQueue
        final MessageQueue queue = me.mQueue;
        
        
        .....

        for (;;) { //进入loop的主循环方法
             Message msg = queue.next(); //可能会阻塞 
             if (msg == null) { //没有消息,则退出循环
                 return;
             }
             //Message通过自身target指向的Handler进行消息分发 
             msg.target.dispatchMessage(msg); 
      
             //将Message放入消息池 
             msg.recycleUnchecked(); 
        }
 }

2.4 假设, MessageQueue中已经有Message了, 那么看看msg.target.dispatchMessage(msg)是怎么执行的.

很容易知道msg.target.dispatchMessage(msg)本质就是handler.dispatchMessage(msg), 在介绍Handler类方法申明的时候, 我们也知道dispatchMessage()是Handler类的的成员.
该方法进行Message消息的分发. 它有对于消息的分发是有优先级的.
1:其中Message类中的Runable类型的callback优先级最高,
2:其次是Handler类中内部Callback接口中handlerMessage()方法的自定义实现,
3:最后才是Handler子类实例化重写handleMessage()方法.
具体可以看代码中的注释.

    public void dispatchMessage(@NonNull Message msg) {
        //第一种: 当Message存在回调方法,优先回调msg.callback.run()方法;
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            //第二种: 当Handler存在Callback成员变量时,回调Handler类内部Callback接口的方法handleMessage();
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //第三种: 优先级最低的是,Handler自身的回调方法handleMessage()
            handleMessage(msg);
        }
    }
第一种: Hadnler类的handleCallback()方法:
 private static void handleCallback(Message message) {
        message.callback.run();
    }
第二种: Handler类中的Callback成员变量和Callback接口
//Callback实际上是Handler类中的一个接口
final Callback mCallback;
//当Handler类实例化的时候, 如果不想通过子类重写Handler类中的handleMessage()方法来实例化的时候
//可以调用Handler类的有参构造函数传入Callback, 构造函数会把mCallback引用到传入的Callback对象.
    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
    }

2.5 那到这里有的同学就会很好奇, 这三种消息处理是在啥时候实现的, 是怎么实现的.

第一种是Handler实例调用post()发送消息的时候
才指定Message.callback (这个callback为Runable类型, 与Handler类中内部Callback接口不是一回事).
Handler类中post()的源码如下:

post()函数中调用了私有函数getPostMessage(Runnable r),
这个函数内部调用Message.obtain()类方法, 从消息池 (就是那个消息链表) 中获得了一个消息, 然后强行指定该消息的callback变量为自己实现的Runable对象, 然后让该消息处理自己想要做的事情.

    public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    //getPostMessage(r)实现
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

第二种和第三种都是在实例化Handler类的时候去重写的,
第二种重写Handler类内部接口Callback里的handleMessage(@NonNull Message msg)方法,
第三种是重写Handler类自身的public void handleMessage(@NonNull Message msg) {}成员方法(源码中对这个方法的解释是:Subclasses must implement this to receive messages).

2.6 发送消息的时候Handler干了啥事

回到2.3节中, 我们再看看Looper.loop()方法, 这个方法中msg.target.dispatchMessage(msg)就是等同于Handler.dispatchMessage(msg).
那Message实例中target对象是什么时候赋值的呢?
为什么我们在App开发过程中直接Handler实例调用sendMessage或者post()就能等着系统回调该Handler实例中自定义好的handleMessage()逻辑了呢?

其实在上面的2.5中, 我们发现Handler.post()内部其实是调用了sendMessageDelayed()的发送消息的方法,Handler类发送消息的方法有很多, 无论是sendMessage, post() 还是sendMessageDelayed() 他们最终都是会调用到Handler类中的enqueueMessage()方法.

enqueueMessage()方法如下:
    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

相信看完这个方法的实现, 所有人都会知道, 默认的msg.target就是this, 就是当前Handler实例.
所以, 只要是发送出去的消息, 每一个普通Message都是有一个默认的target的, 也就是发送消息的Handler实例 (用于拦截同步消息的特殊Message除外).

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352