Android-Handler源码解析-Message

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相关的技术,之后会面向全部开发者,欢迎大家来体验!

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

推荐阅读更多精彩内容