美文网首页
Android-Handler源码解析-Message

Android-Handler源码解析-Message

作者: 张荣旗 | 来源:发表于2022-04-14 16:13 被阅读0次

    Android-Handler源码解析-Message

    源码版本:

    • Handler:SDK-31

    导航:

    成员变量

    // 标识Message
    public int what;
    
    // 存储简单数据,如果存储复杂的数据使用setData()方法。
    public int arg1;
    public int arg2;
    
    // 发送给接收者的任意对象。
    public Object obj;
    
    // 回复给发送者,用于跨进程双向通信。发送message时给其replyTo赋值,接收到该message的进程,可以通过message.replyTo向发送方进程发送message,从而实现双向通信。
    public Messenger replyTo;
    
    public static final int UID_NONE = -1;
    // 可选字段,表示发送消息的uid。这只对Messenger发布(跨进程发布)的消息有效;否则,它就是-1。
    public int sendingUid = UID_NONE;
    // 可选字段,指示导致该消息进入队列的uid。
    public int workSourceUid = UID_NONE;
    
    // 在用标记
    /*package*/ static final int FLAG_IN_USE = 1 << 0;
    // 异步标记
    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
    // copy的清空标记
    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
    // 标记位
    @UnsupportedAppUsage
    /*package*/ int flags;
    
    // 执行时刻,时刻基于SystemClock.uptimeMillis。
    @UnsupportedAppUsage
    public long when;
    
    // 存储复杂的数据
    /*package*/ Bundle data;
    
    // 消息的目标处理器Handler,即由此Handler处理此消息。
    @UnsupportedAppUsage
    /*package*/ Handler target;
    
    // 表示要处理的任务Callback,由Runnable实现,通过handler.post Runnable的时候,Runnable会被存放在Message中,分发的时候再进行回调通知。
    @UnsupportedAppUsage
    /*package*/ Runnable callback;
    
    // 下个消息,用于实现链表结构。
    @UnsupportedAppUsage
    /*package*/ Message next;
    
    // 消息池的同步锁对象,用户消息池的消息获取(obtain()方法)和消息回收(recycleUnchecked()方法)。
    /** @hide */
    public static final Object sPoolSync = new Object();
    // 消息池链表的头,用于操作(增删改查)链表。
    private static Message sPool;
    // 消息池中消息的数量
    private static int sPoolSize = 0;
    // 消息池中消息的最大数量,为50个。
    private static final int MAX_POOL_SIZE = 50;
    // 是否检查消息的回收(recycle()方法),默认true,如果检查并且有问题,则抛出异常。
    private static boolean gCheckRecycle = true;
    

    说明:

    1. Message为什么需要持有Handler,因为Message需要知道是哪个Handler要处理它。
    2. Handler相关介绍,请看Android-Handler源码解析-Handler

    创建Message

    想要使用发送Message,首先要创建Message,所以我们接下来看下它是如何被创建的。

    new Message()

    public Message() {
    }
    

    直接创建一个Message对象,之后可以设置它的属性,未使用复用不推荐使用。

    Message.obtain()

    Message.obtain()

    public static Message obtain() {
        // 使用同步保证线程安全,因为此方法可以在任意线程调用。
        synchronized (sPoolSync) {
            if (sPool != null) {
                // 消息池头不为空,说明队列有内容,从中获取一条消息并返回。缓存的
                Message m = sPool; // 消息池的Head元素
                sPool = m.next; // 缓存池Head为下个元素(移除m)
                m.next = null; // 断开链接
                m.flags = 0; // 清除在用标记
                sPoolSize--; // 池数量减1
                return m; // 返回此消息
            }
        }
        // 消息池头为空,说明队列没有内容,直接创建一条消息并返回。
        return new Message();
    }
    

    Message.obtain()静态方法,内部用了全局消息池,使用了复用推荐使用。

    说明:

    1. 消息池获取消息,它会获取队列第一条消息,并将此消息此队列移除

    Message.obtain(Message)

    public static Message obtain(Message orig) {
        // 获取消息
        Message m = obtain();
        // 将orig的值复制到新消息中
        m.what = orig.what;
        m.arg1 = orig.arg1;
        m.arg2 = orig.arg2;
        m.obj = orig.obj;
        m.replyTo = orig.replyTo;
        m.sendingUid = orig.sendingUid;
        m.workSourceUid = orig.workSourceUid;
        if (orig.data != null) {
            m.data = new Bundle(orig.data);
        }
        m.target = orig.target;
        m.callback = orig.callback;
    
        return m;
    }
    

    Message.obtain()相同,但将现有Message(包括其target)的值复制新的消息中。

    Message.obtain(Handler)

    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
    
        return m;
    }
    

    Message.obtain()相同,但设置了target成员的值。

    Message.obtain(Handler, Runnable)

    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;
    
        return m;
    }
    

    Message.obtain()相同,但设置了targetcallback成员的值。

    Message.obtain(Handler, int)

    public static Message obtain(Handler h, int what) {
        Message m = obtain();
        m.target = h;
        m.what = what;
    
        return m;
    }Message.
    

    Message.obtain()相同,但设置了targetwhat成员的值。

    Message.obtain(Handler, int, Object)

    public static Message obtain(Handler h, int what, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.obj = obj;
    
        return m;
    }
    

    Message.obtain()相同,但设置了targetwhatobj成员的值。

    Message.obtain(Handler, int, int, int)

    public static Message obtain(Handler h, int what, int arg1, int arg2) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;
    
        return m;
    }
    

    Message.obtain()相同,但设置了targetwhatarg1arg2成员的值。

    Message.obtain(Handler, int, int, int, Object)

    public static Message obtain(Handler h, int what,
            int arg1, int arg2, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;
        m.obj = obj;
    
        return m;
    }
    

    Message.obtain()相同,但设置了targetwhatarg1arg2obj成员的值。

    小结

    1. Message创建,有两种方式:new MessageMessage.obtain()Message.obtain()内部使用了复用推荐使用

    回收Message

    由于Message.obtain()方式创建内部使用了复用,所以我们接下来看下它是如何被回收的。

    recycle()

    public void recycle() {
        // 判断是否在使用中
        if (isInUse()) {
            // 在使用中,直接返回,不进行回收。
            if (gCheckRecycle) {
                // gCheckRecycle为true,为检查消息的回收,并且回收时正在使用(有问题),则抛出异常。
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        // 不在使用中,进行回收。
        recycleUnchecked();
    }
    

    recycle()方法,为检查回收,如果此消息处于使用状态,则不进行回收否则调用recycleUnchecked()直接进行回收

    isInUse()方法的使用看后面-其它-InUse,我们接下来看下gCheckRecycle属性,它在updateCheckRecycle()方法内进行更改

    public static void updateCheckRecycle(int targetSdkVersion) {
        if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
            // 小于SDK 21,为false。
            gCheckRecycle = false;
        }
    }
    

    updateCheckRecycle()方法,为更新gCheckRecyclegCheckRecycle默认为trueupdateCheckRecycle()方法在ActivityThreadhandleBindApplication()方法(绑定App)内调用,即gCheckRecycletargetSdkVersion小于21false(不检查),大于、等于21ture(检查)。

    recycleUnchecked()

    void recycleUnchecked() {
        // 将此消息标记为在用,并判断是否添加到消息池中。
        // 清除所有属性
        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) {
                // 小于最大值,则进行添加,把此Message添加到链表的头部。
                next = sPool; // 此Message的next指向链表的Head
                sPool = this; // 链表的Head为此Message 
                sPoolSize++; // 消息池数量加1
            }
        }
    }
    

    recycleUnchecked()方法,为不检查回收,会清除消息的所有属性,以完成释放,并判断如果消息池消息数量没有到达最大数量,则进行添加,以完成回收

    说明:

    1. 消息池添加消息,它会将此消息添加到队列头部,并将此消息作为新的头

    小结

    1. Message回收,有两种方式:recycle()检查回收recycleUnchecked()不检查回收
    2. recycle(),判断了消息如果处于使用状态,则不进行回收否则调用recycleUnchecked()进行回收
    3. recycleUnchecked(),会清除消息的所有属性,并判断如果消息池数量没到消息池最大数量,则进行添加(添加到消息池的链表头部)。

    跨进程通讯

    由于Message实现了Parcelable接口,所以Message可以跨进程传输,所以我们接下来看下它的具体实现。

    writeToParcel()

    public void writeToParcel(Parcel dest, int flags) {
        if (callback != null) {
            // callback不能跨进程传输,抛出异常。
            throw new RuntimeException(
                "Can't marshal callbacks across processes.");
        }
        dest.writeInt(what);
        dest.writeInt(arg1);
        dest.writeInt(arg2);
        if (obj != null) {
            // obj不为空,跨进程传输任意对象,必须实现Parcelable接口,否则抛出异常。
            try {
                Parcelable p = (Parcelable)obj;
                dest.writeInt(1); // 传入1,标记有存入obj对象。
                dest.writeParcelable(p, flags); // 写入obj对象
            } catch (ClassCastException e) {
                throw new RuntimeException(
                    "Can't marshal non-Parcelable objects across processes.");
            }
        } else {
            dest.writeInt(0); // 传入0,标记没有存入obj对象。
        }
        dest.writeLong(when);
        dest.writeBundle(data);
        Messenger.writeMessengerOrNullToParcel(replyTo, dest); // 使用Messenger进行写入
        dest.writeInt(sendingUid);
        dest.writeInt(workSourceUid);
    }
    

    readFromParcel()

    private void readFromParcel(Parcel source) {
        what = source.readInt();
        arg1 = source.readInt();
        arg2 = source.readInt();
        if (source.readInt() != 0) {
            // 有存入obj对象,进行读取。
            obj = source.readParcelable(getClass().getClassLoader());
        }
        when = source.readLong();
        data = source.readBundle();
        replyTo = Messenger.readMessengerOrNullFromParcel(source); // 使用Messenger进行读取
        sendingUid = source.readInt();
        workSourceUid = source.readInt();
    }
    

    小结

    1. Message跨进程通讯callback不能有值,obj如有值必须实现Parcelable接口。

    属性set、get方法

    Messagewhentargetcallbackdata属性对外提供了setget方法,我们接下来看下它们的实现。

    when

    public long getWhen() {
        return when;
    }
    

    target

    public void setTarget(Handler target) {
        this.target = target;
    }
    
    public Handler getTarget() {
        return target;
    }
    

    callback

    public Runnable getCallback() {
        return callback;
    }
    

    data

    public Bundle getData() {
        if (data == null) {
            data = new Bundle();
        }
        return data;
    }
    
    public Bundle peekData() {
        return data;
    }
    
    public void setData(Bundle data) {
        this.data = data;
    }
    

    其它

    copyFrom()

    public void copyFrom(Message o) {
        this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
        this.what = o.what;
        this.arg1 = o.arg1;
        this.arg2 = o.arg2;
        this.obj = o.obj;
        this.replyTo = o.replyTo;
        this.sendingUid = o.sendingUid;
        this.workSourceUid = o.workSourceUid;
    
        if (o.data != null) {
            this.data = (Bundle) o.data.clone();
        } else {
            this.data = null;
        }
    }
    

    指定Message的属性,复制当前Message中。

    sendToTarget()

    public void sendToTarget() {
        target.sendMessage(this);
    }
    

    此消息发送给自己目标Handler,如果未设置此字段,则抛出空指针异常。

    asynchronous(异步)

    public boolean isAsynchronous() {
        return (flags & FLAG_ASYNCHRONOUS) != 0;
    }
    

    isAsynchronous()方法,判断消息是否是异步的,如果是,则返回true

    public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
    }
    

    setAsynchronous()方法,设置消息是否是异步的,这意味着它不受Looper同步屏障的影响。

    说明:

    1. 消息,分为同步消息异步消息默认同步消息
    2. 同步屏障,会屏障同步消息,确保只有异步消息执行,详细请看Android-Handler源码解析-MessageQueue-同步屏障
    3. 某些操作(比如视图失效)可能会在Looper消息队列中引入同步屏障,以阻止后续消息满足某些条件之前被传递。在view invalidation的情况下,调用android.view.View.invalidate后发布的消息会被一个同步屏障挂起,直到下一帧准备被绘制。同步屏障确保在恢复之前完全处理了无效请求。
    4. 异步消息免于同步屏障。它们通常表示中断输入事件其它必须独立处理的信号,即使其它工作已经暂停。
    5. 请注意,异步消息可能是按同步消息顺序交付的,尽管它们之间总是按顺序交付的。如果这些消息的相对顺序很重要,那么它们可能一开始就不应该是异步的,谨慎使用

    InUse

    /*package*/ boolean isInUse() {
        return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
    }
    

    isInUse()方法,判断是否处于使用状态。

    /*package*/ void markInUse() {
        flags |= FLAG_IN_USE;
    }
    

    markInUse()方法,标记处于使用状态。

    总结

    以上就是Handler源码的Message源码部分,Handler其它源码部分看下面导航。之后会出其它Android源码系列,请及时关注。如果你有什么问题,大家评论区见!

    导航:

    最后推荐一下我的网站,开发者的技术博客: devbolg.cn ,目前包含android相关的技术,之后会面向全部开发者,欢迎大家来体验!

    相关文章

      网友评论

          本文标题:Android-Handler源码解析-Message

          本文链接:https://www.haomeiwen.com/subject/zngtertx.html