• 详细讲解Android中的Message的源码


    相信大家对于Android中的Handler是在为熟悉不过了,但是要知道,Handler就其本身而言只是一个壳子,真正在内部起到作用的是Message这个类,对于Message这个类,相信大家也不会陌生,正如大家经常用到的Message.obtain()的方法一样。但是大家又是否知道obtain()方法里面为我们做了哪些操作了,下面我就带领大家进行Message的王国,去一探究竟吧。

    首先映入眼帘的是这样的一行代码:

    1
    public final class Message implements Parcelable


    不用多说,Message实现了Parcelable的接口,也就是说经过Message封装的数据,可以通过Intent与IPC进行传输。既然实现了Parcelable接口,那么在Message方法中必不可少这三个方法:1)writeToParcel 2)describeContents 3)createFromParcel。

    下面我们需要关注的四个成员变量分别是:

    1)public int what

    2)public int arg1

    3)public int arg2

    4)public Object obj


    我们经常是用到这样的几个参数,但是其真实的含义是否真正的理解呢?只要google的内部的注释才是真正的可靠的。

    1)用户定义消息的识别码,以便于系统识别出当前的消息是关于什么的。由于每一个Handle对于自己的消息的识别码都有自己的命名空间。所以我们也就不用担心各个不同的Handler之间会存在冲突的情况。

    2)其中第二个参数与第三个参数的意义是一样的,注释上是这样说明的,这两个参数,如果用户只是需要传输简单的int类型的数据,相比较于setData(Bundle bundle),代价更低。

    3)第四个参数,按照注释上的说明,是这样理解的:这是一个发送给接受者的一个随意的数据,如果使用Messager来进行发送数据进行跨进程的通信,那么当前的obj如果实现了Parcelable就一定不能够为空指针,对于其他的数据的传输,我们一般使用setData方法就可以了。但是需要注意的是,对于高于android.os.Build.VERSION_CODES#FROYO的版本,这里的Parcelable对象是不支持的。


    上面的变量讲完了以后,接下来,我们还需要讲解另外的一组常量的定义:

    1
    2
    3
    4
    5
    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    //池塘里最大的尺寸
    private static final int MAX_POOL_SIZE = 50;


    乍一看,大家可能不理解上面四个变量的定义,如果我提醒一下大家,Android中的Message是可以重用的,那么相信大家就能够大致猜测到这四个变量的含义了。

    1、第一个变量其实就是充当锁的作用,避免多线程争抢资源,导致脏数据

    2、sPool这个变量可以理解为消息队列的头部的指针

    3、sPoolSize是当前的消息队列的长度

    4、定义消息队列缓存消息的最大的长度。

    Ok,到这里,Message的成员变量已经讲解完毕,接下来主要是讲解其中的Api方法。

    第一个方法就是大家经常用到的obtain方法,而且不带任何的参数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    sPoolSize--;
                    return m;
                }
            }
            return new CustomMessage();
        }www.2cto.com

    首先为了避免多线程进行争抢资源,给sPoolSync进行加锁。首先判断当前的队列的指针是否为空,如果当前的指针已经不为空,当前的队列的头部的消息就是可以重用并且被取出,那么当前的队列的头指针指向当前的消息的下一个消息,也就是m.next,同时把取出的消息的尾部指针置为null,队列的长度减1.

    第二个方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public static Message obtain(Message orig) {
            Message m = obtain();
            m.what = orig.what;
            m.arg1 = orig.arg1;
            m.arg2 = orig.arg2;
            m.obj = orig.obj;
            m.replyTo = orig.replyTo;
            if (orig.data != null) {
                m.data = new Bundle(orig.data);
            }
            m.target = orig.target;
            m.callback = orig.callback;
            return m;
        }

    我们可以看到,这个方法相对于上面的方法多了一个orig的参数,从上面的代码我们可以看到,首先从队列中取出Message的对象,然后对其中的参数的对象的各个数据进行逐一的拷贝,并最终返回对象。

    第三个方法如下:

    1
    2
    3
    4
    5
    public static Message obtain(Handler h) {
            Message m = obtain();
            m.target = h;
            return m;
        }

    不用多说,这个函数中为当前创建的消息指定了一个Handler对象,因为我们知道Handler是Message的最终的目的地。

    1
    2
    3
    4
    5
    6
    public static Message obtain(Handler h, Runnable callback) {
            Message m = obtain();
            m.target = h;
            m.callback = callback;
            return m;
        }

    相对于上面的方法,这里面多了一行m.callback = callback,按照注释上面的说明,当一些消息真正的被执行的时候,callback这个Runnbale方法将会被触发执行的。

    接下来有一系列的方法的逻辑是差不多的,我们取其中的一个进行讲解:

    1
    2
    3
    4
    5
    6
    7
    8
    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的时候,顺便可以为这个Message提供一些逻辑上需要的参数。

    消息有创建,那么就必然存在回收的概念,下面我们一起来看一下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public void recycle() {
            clearForRecycle();
            synchronized (sPoolSync) {
                if (sPoolSize < MAX_POOL_SIZE) {
                    next = sPool;
                    sPool = this;
                    sPoolSize++;
                }
            }
        }
    void clearForRecycle() { flags = 0; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; when = 0; target = null; callback = null; data = null; }



    1
     

    在clearForRecycle这个函数中,是做一些回收的预处理的操作,该置为0的参数置为0,该置为null的参数置为null。在recycle的函数中,只要当前的缓存的队列的长度没有超过上限,将当前的消息添加到队列的尾部。

    下面的方法是关于实现Parcelable所需要的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    public static final Parcelable.Creator<custommessage> CREATOR
                = new Parcelable.Creator<custommessage>() {
            public Message createFromParcel(Parcel source) {
                Message msg = Message.obtain();
                msg.readFromParcel(source);
                return msg;
            }
             
            public Message[] newArray(int size) {
                return new Message[size];
            }
        };
             
        public int describeContents() {
            return 0;
        }
     
        public void writeToParcel(Parcel dest, int flags) {
            if (callback != null) {
                throw new RuntimeException(
                    "Can't marshal callbacks across processes.");
            }
            dest.writeInt(what);
            dest.writeInt(arg1);
            dest.writeInt(arg2);
            if (obj != null) {
                try {
                    Parcelable p = (Parcelable)obj;
                    dest.writeInt(1);
                    dest.writeParcelable(p, flags);
                } catch (ClassCastException e) {
                    throw new RuntimeException(
                        "Can't marshal non-Parcelable objects across processes.");
                }
            } else {
                dest.writeInt(0);
            }
            dest.writeLong(when);
            dest.writeBundle(data);
            Messenger.writeMessengerOrNullToParcel(replyTo, dest);
        }
     
        private void readFromParcel(Parcel source) {
            what = source.readInt();
            arg1 = source.readInt();
            arg2 = source.readInt();
            if (source.readInt() != 0) {
                obj = source.readParcelable(getClass().getClassLoader());
            }
            when = source.readLong();
            data = source.readBundle();
            replyTo = Messenger.readMessengerOrNullFromParcel(source);
        }</custommessage></custommessage>

    Ok,Message的内核源码的剖析就讲解到这里,相信大家以后再用到这个类的时候会有更深的理解啦。

    结伴旅游,一个免费的交友网站:www.jieberu.com

    推推族,免费得门票,游景区:www.tuituizu.com

  • 相关阅读:
    C
    A
    枚举子集的几种方法
    Codeforces Round #476 (Div. 2) [Thanks, Telegram!] ABCDE
    wannafly挑战赛14
    2018西安电子科大同步赛
    概率dp学习记录
    xcoj 1103 插线板(树链刨分求最大子段和)
    bzoj 2286(虚树+树形dp) 虚树模板
    bzoj3012(Trie)
  • 原文地址:https://www.cnblogs.com/rabbit-bunny/p/4200381.html
Copyright © 2020-2023  润新知