Android Message源码阅读

Handler是Android中一种处理线程消息循环的机制,而 Message 是Handler用来放消息的包装。总得来说,Message作为一个用于封装消息的对象,逻辑并不复杂。

public final class Message implements Parcelable {
    // Source Code here
}

Android常用序列化有 Serializable 和 Parcelable 两种,该类继承Parcelable接口表明支持序列化。简单说,前者用的时间比较长且范围更广,但是序列化过程中产生大量小对象;后者性能好,但是需要手动实现4个必须方法,范围也没有前者广。

一、成员变量

用一个标志来区分不同的消息的身份。不同的Handler里使用相同what值的不同消息不会弄混。一般用十六进制形式表示,阅读起来比较容易。

public int what; // 0x01

arg1arg2都是类中可选的变量存储位置,可以方便地用来存放两个数值,这样就不用访问obj对象就能读取变量。

public int arg1; 
public int arg2;

obj用来保存一个需要的对象,在处理的时候取出来使用。

public Object obj; // 用来保存对象
public Messenger replyTo; // 回复跨进程的Messenger
public int sendingUid = -1; // Messenger发送时使用

static final int FLAG_IN_USE = 1 << 0; // 正在使用标志值
static final int FLAG_ASYNCHRONOUS = 1 << 1; // 异步标志值
static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;

int flags; // 消息标志,上面三个常量 FLAG_* 用在这里
long when; // 估计和arg1、arg2性质一样,存时间戳

Bundle data;    // 存放Bundle
Handler target; // 存放Handler实例
Runnable callback; // 消息的回调操作
Message next;   // 消息池用链表的方式存储

private static final Object sPoolSync = new Object(); // 消息池同步公用标志
private static Message sPool; // 消息池
private static int sPoolSize = 0; // 消息池已缓存数量
private static final int MAX_POOL_SIZE = 50; // 消息池最大容量
private static boolean gCheckRecycle = true; // 该版本系统是否支持回收标志位

二、消息体获取

从消息池里取可以复用的消息对象。方法体有一个同步代码块,对象sPoolSync作为锁标志,避免不同线程取到同一个消息体导致消息紊乱。如果没有可复用的对象,就新建一个消息体返回。

我们可以手动创建一个消息对象,但是最好从obtain()中获取缓存好的消息体,避免造成多余对象创建。

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // 移除使用中标志
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

从消息池中取可用的消息体,然后把形参全部复制进去。

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;
    m.sendingUid = orig.sendingUid;
    if (orig.data != null) {
        m.data = new Bundle(orig.data);
    }
    m.target = orig.target;
    m.callback = orig.callback;

    return m;
}

返回一个消息,这个消息已经设置好发送的目标

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

    return m;
}

下面都是获取消息体然后设置消息体参数的方法,就是public static Message obtain()的重载。

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

    return m;
}

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

    return m;
}

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

    return m;
}

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

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

三、消息回收

检查系统是否支持消息对象循环回收

public static void updateCheckRecycle(int targetSdkVersion) {
    if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
        gCheckRecycle = false;
    }
}

对象使用完毕就会调用recycleUnchecked()

public void recycle() {
    if (isInUse()) {
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        return;
    }
    recycleUnchecked();
}

消息体清空回收

void recycleUnchecked() {
    // 当消息还在回收池中,添加正在使用标志位,其他情况就清除掉
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;
    // 最多缓存50个消息体,多余的GC
    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

四、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;

    if (o.data != null) {
        this.data = (Bundle) o.data.clone();
    } else {
        this.data = null;
    }
}

五、Getter & Setter

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

public Handler getTarget() {
    return target;
}

public long getWhen() {
    return when;
}

public Runnable getCallback() {
    return callback;
}

public Bundle getData() {
    if (data == null) {
        data = new Bundle();
    }
    
    return data;
}
// peekData()对比getData()
public Bundle peekData() {
    return data;
}

public void setData(Bundle data) {
    this.data = data;
}
// 把消息发送到Handler,配合obtain()使用,这样target不为空。
public void sendToTarget() {
    target.sendMessage(this);
}

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

六、标志位

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

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

void markInUse() {
    flags |= FLAG_IN_USE;
}

七、Parcelable实现

实现Parcelable接口的四个固定方法 CREATORdescribeContents()writeToParcel()readFromParcel()

public static final Parcelable.Creator<Message> CREATOR
        = new Parcelable.Creator<Message>() {
    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);
    dest.writeInt(sendingUid);
}

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);
    sendingUid = source.readInt();
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容