关于Hander之sendMessage和obtainMessage的区别

别人家的帖子千千万,不如自己源码走一遍,以下说明分精华和详细,精华就是结论,详细是源码刨析+图!!

精华

1.sendMessage
public final boolean sendMessage(@NonNull Message msg); //传入一个Message参数,进行排队发送到handleMessage
2.
public final Message obtainMessage(); //返回值是一个Message,一般搭配sendToTarget使用
有多个重载版本,就是构建传入参数的不同产出不同的Message
public final Message obtainMessage(int what);   //带指定what的Message
public final Message obtainMessage(int what, @Nullable Object obj);//带指定what和obj的Message
public final Message obtainMessage(int what, int arg1, int arg2);//带指定what arg1 arg2的Message
public final Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj);带指定what arg1 arg2 obj的Message

搭配使用就是 obtainMessage(xx).sendToTarget(); //实现和sendMessage相同的功能
3.两者区别
obtainMessage会利用内部的message池,如果池中有可用message,就不重新new分配,直接复用,达到节省开销的目的
看源码发现执行sendToTarget内部是和sendMessage相同的
所以平常使用推荐obtainMessage

详细

来到详细说一下内部实现

obtainMessage的实现

1.@NonNull
public final Message obtainMessage()
{
    return Message.obtain(this);
}
源码调用Message的obtain函数,然后再进入
2.public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;
    return m;
}
obtain()函数返回一个Message, 然后将参数的hander保存在message中
3.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是否有数值,sPool的原型是
private static Message sPool; 一个message
如果有数值
做了一个链表的操作
看图

就是这样,sPool又指向了下一个空闲,直到等于NULL, 就会重新分配空间

那么message返回了之后呢,调用的sendToTarget是啥?

看message源码
1.public void sendToTarget() {
    target.sendMessage(this);
}
target就是我们obtain保存的我们的Hander
到这里就已经和直接调用sendMessage相同了,只不过message可能使用池中的而已

那这个池是怎么形成的?

复杂些
首先看message的源代码的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 = UID_NONE;
    workSourceUid = UID_NONE;
    when = 0;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}
在这里我们看到了首先是对标志和成员变量的复位
把当前message的next赋值为pool的值,如果pool是null也就是第一次分配new得到的message,那么这里next就为null
然后把当前messgae this赋值为sPool,内存增加
看图

这仅仅是一个message调用recycleUnchecked发生的情况,如果有很多个sendmessage都发送,recycleUnchecked就会被多次调用,那么就会形成第一张图的message缓存池

在哪里调用的recycleUnchecked?

首先看Handler的
1.public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}
调用了sendMessageDelayed
2.进入
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
调用sendMessageAtTime,将msg一直传入
3.进入
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}
MessageQueue就是消息队列
4.进入
    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);
}
把当前的hander对象保存,和之前是一样的
我们这里的enqueueMessage函数就不进入了,看乱了不好,你需要知道在enqueueMessage里面会把这个发送的msg进行保存,等待使用
enqueueMessage里面成员变量mMessages存储的永远是最先进入的message,然后根据next一个一个找
看一张图了解下

就是一个链表,如果是第一个直接赋值
如果不是,那么不断遍历找到最后一个,把msg插入到最后一个

谁使用enqueueMessage的内容?

就是Looper了
进入Looper的loop()函数
1.发现里面的有一个for循环,在这个for循环里面不断把enqueueMessage的数据拿出来进行handermessage的处理
Message msg = queue.next(); // 就是从enqueueMessage里面拿数据
2.下面的
msg.target.dispatchMessage(msg);
target就是我们之前传入的hander,这里就执行了dispatchMessage函数,这个函数先不说,当它执行了handermessage
3.最后有一个
msg.recycleUnchecked();
重点来了,创建出的msg没有被销毁,调用了recycleUnchecked进行保存,spool指向这个message,然后不断recycleUnchecked被调用,汇聚成了message的缓存池
来个小图

以上仅仅是浅浅的刨析,不喜勿喷

posted @ 2020-06-02 22:54  make_wheels  阅读(692)  评论(0编辑  收藏  举报