在Android中为啥建议你用Message.obtain()方法获取Message对象,而不是使用new关键字?

大家都知道Android应用程序是通过消息来驱动的。每开启一个应用进程,我们都会在当前应用进程的主线程成创建一个消息队列,和Handler来处理应用程序中的消息。不管是刷新界面,或者是启动组件等等,都和消息相关。所以Message对象在我们的应用程序中使用时很频繁的。
一般情况下,我们会通过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()方法呢?下面我将从源码角度,带大家了解一下其中的奥妙。

既然它建议我们使用obtain()方法,那我们就来看看它为我们做了些什么事。
以下源码都源码基于Android8.0。

1.解析Message.obtain()方法:

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

可以看到在obtain方法中,如果sPool不为null的时候,还是会调用new Message()来创建一个Message对象。那么这个sPool又是个什么东西呢?其实呢从上面代码我们就可以看出来,它其实是一个链表。每个Message对象都有next字段,它的类型也是Message。上面的代码其实就是把链表中的第一个节点返回,然后把sPool置为第一个Messgae的next对象,即下一个节点。那我们这个sPool对象是怎么生成的呢?我们在来找一找答案。我们在Message类中搜索一下sPool这个对象,看看什么时候会往它里面添加Message对象。我们可以找到recycleUnchecked()方法,recycleUnchecked()是在recycle()方法中被调用的。

2.解析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;
        }
        //可以回收调用recycleUnchecked()方法
        recycleUnchecked();
    }


  void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        //清除Message对象里面成员变量的信息
        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) {
           //当sPoolSize大小小于MAX_POOL_SIZE即50时,继续往池中添加。
           //sPoolSize可以理解为Message池内Messgae对象的数量。
            if (sPoolSize < MAX_POOL_SIZE) {
                //当前Message对象当作第一个链表的第一个节点
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

好了现在我们知道了当Message对象被回收的时候,会像池中添加一个Messgae对象。那么什么时候会回收一个Messsge对象呢。可以猜想以下,一个对象不用了之后才会被回收,在Android消息机制中,Looper会通过loop()方法想消息队列中去消息,我们去loop()方法中找找,看看是否会调用我们Message的回收方法。

3.解析Looper.loop()方法

源码如下:

 public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;

       //无限循环
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

          //省略部分代码
          .....
          .....
 
            try {
               //调用Handler的dispatchMessage来处理消息
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            
     //此处省略部分代码
     .....
     .....
            //调用回收处理完成的Message的方法
            msg.recycleUnchecked();
        }
    }

可以清楚的看到当我们的Looper对象处理完我们的Message时,会去调用回收Message的方法,向我们的Message对象中的复用池中添加一个Message对象。

4.总结

分析到这里,我相信所有朋友都明白了。Message类里维护了一个sPool对象。可以理解成一个Message链表,这个链表默认的最大长度是50。在Android消息机制中,每当一个Message对象被处理完成之后,都会被放入这个池中,为我们提供了复用。当我们调用Message.obtain()方法时,如果复用池中存在Message对象,我们就不会去创建一个新的Message对象。这样就避免了频繁创建和销毁Messgae对象的带来的性能开销。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值