Handler系列—Message对象的获取机制

Posted datian1234

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Handler系列—Message对象的获取机制相关的知识,希望对你有一定的参考价值。

为什么android建议Message.obtain(),而不是直接用new关键字

前言
一般情况下,用Handler发送一个消息,即Message对象,可以通过new关键字创建,也可以通过Message.obtain()创建,在Android源码中,Message的构造方法上边可以看到如下注释:

/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
*/
public Message() {
}

注释意思是:首选Message.obtain()来获取Message对象

为什么Android要推荐使用Message.obtain()方法来获取呢???让我们来看看源码里的奥妙,一起来fucking the source code

源码分析Message.obtain()方法

/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 *
 * 从一个pool里获取Message实例,允许我们在多种情况下,避免分配新的对象
 */
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

可以看到,如何sPool对象为null,则new一个Message对象;如果不为空,就把sPool的第一个节点对象返回,然后把sPool指向下一个节点。那么sPool到底是什么呢???其实他就是一个Message链表,每个Message里都有一个next字段,类型也是Message对象。

到这里也看到了Message对象的获取方式分为两种情况

总结:

如果sPool为null,则直接通过new来获取
如果sPool不为null,则将sPool的第一个节点返回,然后把sPool指向其下一个节点
那么这里又有一个问题,sPool是什么时候生成和赋值的,什么时候向sPool池中添加Message对象的呢?接下来我们通过源码定位到了recycleUnchecked()这个方法,这个方法又是在recycle()方法中调用的

源码解析recycle()和recycleUnchecked()方法

public void recycle() {
    //判断当前message是否可以被回收(是否正在被使用)
    if (isInUse()) {
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        return;
    }
    
    //当前message对象可以被回收
    recycleUnchecked();
}

void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    
    //这些就是清除当前message对象的成员变量信息(相当于一个新的message)
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = UID_NONE;
    workSourceUid = UID_NONE;
    when = 0;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
        //当前池中的size小于最大size,就为池中添加当前message
        //MAX_POOL_SIZE 大小默认为50
        if (sPoolSize < MAX_POOL_SIZE) {
        		//当前message作为sPool的第一个节点
            next = sPool;
            //再将当前message赋值给sPool
            sPool = this;
            sPoolSize++;
        }
    }
}

这两个方法的源码还是比较容易看懂的,基本总结就是当message对象被回收的时候,先清除其成员变量信息,然后将该对象作为sPool池的第一个节点添加到池中。

那么问题又来了,message对象什么时候被回收呢???咱们可以猜想一下,当message对象不用了之后被回收;那么在Android的消息机制中,是通过Looper.loop()方法从消息队列中取出消息进行处理,那么咱们就去这个方法里去寻找我们想要的答案

解析Looper.loop()

public static void loop() {
    final Looper me = myLooper();
    
    //首先判断Looper对象为空,则直接抛异常
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        
    //省略部分代码
    ......
    ......
    
    //通过looper拿到消息队列
    final MessageQueue queue = me.mQueue;

    //省略部分代码
    ...
    ...
    
		//开启循环来从消息队列中取消息
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        //省略部分代码
        ......
        ......

        try {
        		//将message对象交给hadler的dispatchMessage方法进行处理消息
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            ThreadLocalWorkSource.restore(origWorkSource);
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        
        //省略部分代码
        ......
        ......

				//终于。。。看到了我们熟悉的方法了
        msg.recycleUnchecked();
    }
}

通过源码,可以清楚的看到,当处理完消息后,会调用Message的回收方法,即在sPool里添加一个消息

总结
源码里可以看到,Message对象里维护了一个sPool对象,其实就是一个Message链表,默认长度为50。在消息机制中,每次obtain()获取Message对象的时候,如果sPool为空,则直接new一个出来,如果不为空,则取第一个节点的message对象;然后在Looper.loop()里进行轮询处理消息的时候,每次处理完一个消息,就把该消息插入到sPool的第一个节点中去。这样设计的目的就是维护一个消息池,如果池中有复用的Message,则拿出来复用,避免频繁创建和销毁Message带来的内存和性能消耗。

Android系统中,每个应用程序是由Android的Activity,service,Broadcast,contentProvider这四剑客的中一个或多个组合而成,这四剑客所涉及的多进程间的通信底层都是依赖于Binder lPC机制。例如当进程A中的Activity要向进程B中的Service通信,这便需要依赖于Binder IPC。不仅于此,整个Android系统架构中,大量采用了Binder机制作为IPC(进程间通信)方案,当然也存在部分其他的IPC方式,比如Zygote通信便是采用socket。

Binder作为Android系统提供的一种IPC机制,无论从事系统开发还是应用开发,都应该有所了解,这是Android系统中最重要的组成,也是最难理解的一块知识点,错综复杂。要深入了解Binder机制,最好的方法便是阅读源码,借用Linux鼻祖Linus Torvalds曾说过的一句话: Read The Fucking source code 。

以上完整学习笔记pdf全部免费分享,需要的朋友只需要点赞支持一下后,【点击这里直达免费获取方式】。

以上是关于Handler系列—Message对象的获取机制的主要内容,如果未能解决你的问题,请参考以下文章

Android Handler消息机制03-Message源码学习

Android的Handler深入解析

Android_Handler机制

Android Handler消息机制01-Message源码学习

Android的handler机制的原理?

Handler机制中的五大对象