android6.0源码解析-handler、message和looper(一)-message

原创
2017/01/07 11:44
阅读数 144

 

   message相对handler来说,是一个携带数据的消息类。但是本身message是一个(后进先出)链表结构的类。设计成链表的形式是为了做一个对象池,减少对象的创建和销毁,提高效率。

   在message源码里面,最主要的方法只有两个。一个是获取对象对obtain(),一个是回收对象的recycle()。其他的方法都很简单。

    obtain的作用是判断当前的Message的对象池中,是否还有对象。如果没有则创建,如果有则取一个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();
    }

这是一个静态方法,首先进入sPoolSync同步块,然后判断静态变量sPool(该变量就是message对象池这个链表的表头)是否是null。如果是null,则说明当前根本没有创建有Message对象(注意:obtain是静态方法,所Message在没有被创建时,也可以被调用)。此时直接返回一个new Message()。如果不是null,则在对象池的链表头部,取出一个对象,并返回。

 

   recycle()的作用就是检查对象是否可以被回收。是,则将对象里面的数据初始化,然后存入对象池。否,则不做任何处理(版本信息不同,可能会有异常,涉及到外部代码的调用。暂时讲解,后续遇到会详细介绍)。recycle()源码如下:

    public void recycle() {
        if (isInUse()) {//判断标识flags,是否处于被使用状态,是则返回true
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }

进入方法,首先会判断当前message是否在使用状态中。其中被使用,会调用if(isInUse()),然后什么都不做,直接返回。其中gCheckRecycle如果为true会抛出异常。gCheckRecycle在message初始化时,默认是为true。只有在updateCheckRecycle(int targetSdkVersion)方法时,才会被设置为false。

    public static void updateCheckRecycle(int targetSdkVersion) {
    //当前版本低于21时,gCheckRecycle为false,否则gCheckRecycle为true
        if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
            gCheckRecycle = false;
        }
    }

  代码很简单,版本号低于21时,    gCheckRecycle会设置为false。至于什么时候,以及为什么会调用方法updateCheckRecycle,后续我们涉及到的时候会再详细讲解。

当isInUse()返回false时,会调用方法recycleUnchecked();真正将对象回收到对象池中,就是通过该方法。

    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

从源代码中可以看出,当前对象被回收到对象池中,flags会被重置为被使用中(为什么不重置为没有被使用的状态了?),其他变量都进行了初始化。

进入同步模块了(sPoolSync),对象池中,对象的数量不大于MAX_POOL_SIZE(值是50)的数量,则将新对象放在链表的头部。

这里的sPool是一个Message对象,如果值不是null,则sPool是指向当前对象。next也是一个Message对象,如果值不为null,则next是指向下一个Message对象。通过next,就形成了一个单向链表。用图来尸解一下这个过程

如果使用new Message()来创建对象,sPool和next都是null,没什么可以分析的。我们只分析对象池中有对象的情况。

 

OK,现在两个方法都分析了,现在开始我们将单个对象从被创建,然后存入对象池,然后再被循环使用的流程画出来。

1,第一次调用obtain时,首先判断sPool表头是null,表明此时没有Message对象。我们通过new Message()获得一个对象。

2,此时Message对象被回收到对象池中(调用方法recycleUnchecked()),此时的sPool作为对象池的表头,就指向Message对象。

3,好了,此时如果我们再调用obtain方法。进入方法的同步模块,会首先判断sPool是否为null,如果不是,则重置flag标识为未被使用,将sPool传递给一个新的对象m(对象m没有被创建,只是一个索引,和sPool指向同一个对象。),再将next传递给sPool(next一直都是null)。此时,m拥有了一个对象,mSpool和next再次变为null。

此时obtain返回message对象。

 

什么情况下,会在对象池中产生多个对象了?

1,此时对象池中没有任何对象,我们调用obtain,会new Message()。此时的message对象如果没有调用方法recycle(),则我们再调用obtain时,又会new Message();多次调用obtain,此时内存中产生了N个message的对象,但是互相并没有联系。就像下面这样。

2,下面,其中一个(比如对象1)对象被调用了recycleUnchecked()方法,表示该对象可以放在对象池了。则变成了下面的样子

看没,表头sPool指向了对象1。

3,如果此时,对象2也被调用了recycleUnchecked()方法,代码还记得嘛?

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }

先将表头sPool指向的对象赋值给对象2的next,然后再让表头sPool指向对象2.变成了下面的样子

后进去的,排在最前面。

4,依次调用对象3、对象4的recycleUnchecked()方法,则此时的对象池是下面这样

5,这下对象池是不是有对象了?好吧,此时我们开始调用obtain了,还记得对象池有对象时,调用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;
            }
        }

首先检查表头sPool是指向的null还是指向一个对象。我们是有对象的人,对吧?所以将sPool指向的对象(看上面4图,现在sPool指向的是对象4)从对象池中提取出来并返回。

看没,对象4被obtain返回了,不在对象池中。此时的对象池只剩下对象3、对象2、对象1.

依次再调用obtain了,还会从对象池中取出对象。直到对象池中再无对象,obtain就会创建新的对象,如果被回收,则会再次被放在对象池中。

message源码的分析,大体就到这。其他方法没有分析,是因为其他方法简单,一看就能看懂。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部