• Android-Handler源码解析-Message


    成员变量

    1. // 标识Message
    2. public int what;
    3. // 存储简单数据,如果存储复杂的数据使用setData()方法。
    4. public int arg1;
    5. public int arg2;
    6. // 发送给接收者的任意对象。
    7. public Object obj;
    8. // 回复给发送者,用于跨进程双向通信。发送message时给其replyTo赋值,接收到该message的进程,可以通过message.replyTo向发送方进程发送message,从而实现双向通信。
    9. public Messenger replyTo;
    10. public static final int UID_NONE = -1;
    11. // 可选字段,表示发送消息的uid。这只对Messenger发布(跨进程发布)的消息有效;否则,它就是-1
    12. public int sendingUid = UID_NONE;
    13. // 可选字段,指示导致该消息进入队列的uid。
    14. public int workSourceUid = UID_NONE;
    15. // 在用标记
    16. /*package*/ static final int FLAG_IN_USE = 1 << 0;
    17. // 异步标记
    18. /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
    19. // copy的清空标记
    20. /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
    21. // 标记位
    22. @UnsupportedAppUsage
    23. /*package*/ int flags;
    24. // 执行时刻,时刻基于SystemClock.uptimeMillis。
    25. @UnsupportedAppUsage
    26. public long when;
    27. // 存储复杂的数据
    28. /*package*/ Bundle data;
    29. // 消息的目标处理器Handler,即由此Handler处理此消息。
    30. @UnsupportedAppUsage
    31. /*package*/ Handler target;
    32. // 表示要处理的任务Callback,由Runnable实现,通过handler.post Runnable的时候,Runnable会被存放在Message中,分发的时候再进行回调通知。
    33. @UnsupportedAppUsage
    34. /*package*/ Runnable callback;
    35. // 下个消息,用于实现链表结构。
    36. @UnsupportedAppUsage
    37. /*package*/ Message next;
    38. // 消息池的同步锁对象,用户消息池的消息获取(obtain()方法)和消息回收(recycleUnchecked()方法)。
    39. /** @hide */
    40. public static final Object sPoolSync = new Object();
    41. // 消息池链表的头,用于操作(增删改查)链表。
    42. private static Message sPool;
    43. // 消息池中消息的数量
    44. private static int sPoolSize = 0;
    45. // 消息池中消息的最大数量,为50个。
    46. private static final int MAX_POOL_SIZE = 50;
    47. // 是否检查消息的回收(recycle()方法),默认true,如果检查并且有问题,则抛出异常。
    48. private static boolean gCheckRecycle = true;
    49. 复制代码

    说明:

    1. Message为什么需要持有Handler,因为Message需要知道是哪个Handler要处理它。

    创建Message

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

    new Message()

    1. public Message() {
    2. }
    3. 复制代码

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

    Message.obtain()

    Message.obtain()

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

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

    说明:

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

    Message.obtain(Message)

    1. public static Message obtain(Message orig) {
    2. // 获取消息
    3. Message m = obtain();
    4. // 将orig的值复制到新消息中
    5. m.what = orig.what;
    6. m.arg1 = orig.arg1;
    7. m.arg2 = orig.arg2;
    8. m.obj = orig.obj;
    9. m.replyTo = orig.replyTo;
    10. m.sendingUid = orig.sendingUid;
    11. m.workSourceUid = orig.workSourceUid;
    12. if (orig.data != null) {
    13. m.data = new Bundle(orig.data);
    14. }
    15. m.target = orig.target;
    16. m.callback = orig.callback;
    17. return m;
    18. }
    19. 复制代码

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

    Message.obtain(Handler)

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

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

    Message.obtain(Handler, Runnable)

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

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

    Message.obtain(Handler, int)

    1. public static Message obtain(Handler h, int what) {
    2. Message m = obtain();
    3. m.target = h;
    4. m.what = what;
    5. return m;
    6. }Message.
    7. 复制代码

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

    Message.obtain(Handler, int, Object)

    1. public static Message obtain(Handler h, int what, Object obj) {
    2. Message m = obtain();
    3. m.target = h;
    4. m.what = what;
    5. m.obj = obj;
    6. return m;
    7. }
    8. 复制代码

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

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

    1. public static Message obtain(Handler h, int what, int arg1, int arg2) {
    2. Message m = obtain();
    3. m.target = h;
    4. m.what = what;
    5. m.arg1 = arg1;
    6. m.arg2 = arg2;
    7. return m;
    8. }
    9. 复制代码

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

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

    1. public static Message obtain(Handler h, int what,
    2. int arg1, int arg2, Object obj) {
    3. Message m = obtain();
    4. m.target = h;
    5. m.what = what;
    6. m.arg1 = arg1;
    7. m.arg2 = arg2;
    8. m.obj = obj;
    9. return m;
    10. }
    11. 复制代码

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

    小结

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

    回收Message

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

    recycle()

    1. public void recycle() {
    2. // 判断是否在使用中
    3. if (isInUse()) {
    4. // 在使用中,直接返回,不进行回收。
    5. if (gCheckRecycle) {
    6. // gCheckRecycle为true,为检查消息的回收,并且回收时正在使用(有问题),则抛出异常。
    7. throw new IllegalStateException("This message cannot be recycled because it "
    8. + "is still in use.");
    9. }
    10. return;
    11. }
    12. // 不在使用中,进行回收。
    13. recycleUnchecked();
    14. }
    15. 复制代码

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

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

    1. public static void updateCheckRecycle(int targetSdkVersion) {
    2. if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
    3. // 小于SDK 21,为false。
    4. gCheckRecycle = false;
    5. }
    6. }
    7. 复制代码

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

    recycleUnchecked()

    1. void recycleUnchecked() {
    2. // 将此消息标记为在用,并判断是否添加到消息池中。
    3. // 清除所有属性
    4. flags = FLAG_IN_USE;
    5. what = 0;
    6. arg1 = 0;
    7. arg2 = 0;
    8. obj = null;
    9. replyTo = null;
    10. sendingUid = UID_NONE;
    11. workSourceUid = UID_NONE;
    12. when = 0;
    13. target = null;
    14. callback = null;
    15. data = null;
    16. // 同步,保证线程安全。
    17. synchronized (sPoolSync) {
    18. if (sPoolSize < MAX_POOL_SIZE) {
    19. // 小于最大值,则进行添加,把此Message添加到链表的头部。
    20. next = sPool; // 此Message的next指向链表的Head
    21. sPool = this; // 链表的Head为此Message
    22. sPoolSize++; // 消息池数量加1
    23. }
    24. }
    25. }
    26. 复制代码

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

    说明:

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

    小结

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

    跨进程通讯

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

    writeToParcel()

    1. public void writeToParcel(Parcel dest, int flags) {
    2. if (callback != null) {
    3. // callback不能跨进程传输,抛出异常。
    4. throw new RuntimeException(
    5. "Can't marshal callbacks across processes.");
    6. }
    7. dest.writeInt(what);
    8. dest.writeInt(arg1);
    9. dest.writeInt(arg2);
    10. if (obj != null) {
    11. // obj不为空,跨进程传输任意对象,必须实现Parcelable接口,否则抛出异常。
    12. try {
    13. Parcelable p = (Parcelable)obj;
    14. dest.writeInt(1); // 传入1,标记有存入obj对象。
    15. dest.writeParcelable(p, flags); // 写入obj对象
    16. } catch (ClassCastException e) {
    17. throw new RuntimeException(
    18. "Can't marshal non-Parcelable objects across processes.");
    19. }
    20. } else {
    21. dest.writeInt(0); // 传入0,标记没有存入obj对象。
    22. }
    23. dest.writeLong(when);
    24. dest.writeBundle(data);
    25. Messenger.writeMessengerOrNullToParcel(replyTo, dest); // 使用Messenger进行写入
    26. dest.writeInt(sendingUid);
    27. dest.writeInt(workSourceUid);
    28. }
    29. 复制代码

    readFromParcel()

    1. private void readFromParcel(Parcel source) {
    2. what = source.readInt();
    3. arg1 = source.readInt();
    4. arg2 = source.readInt();
    5. if (source.readInt() != 0) {
    6. // 有存入obj对象,进行读取。
    7. obj = source.readParcelable(getClass().getClassLoader());
    8. }
    9. when = source.readLong();
    10. data = source.readBundle();
    11. replyTo = Messenger.readMessengerOrNullFromParcel(source); // 使用Messenger进行读取
    12. sendingUid = source.readInt();
    13. workSourceUid = source.readInt();
    14. }
    15. 复制代码

    小结

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

    属性set、get方法

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

    when

    1. public long getWhen() {
    2. return when;
    3. }
    4. 复制代码

    target

    1. public void setTarget(Handler target) {
    2. this.target = target;
    3. }
    4. public Handler getTarget() {
    5. return target;
    6. }
    7. 复制代码

    callback

    1. public Runnable getCallback() {
    2. return callback;
    3. }
    4. 复制代码

    data

    1. public Bundle getData() {
    2. if (data == null) {
    3. data = new Bundle();
    4. }
    5. return data;
    6. }
    7. public Bundle peekData() {
    8. return data;
    9. }
    10. public void setData(Bundle data) {
    11. this.data = data;
    12. }
    13. 复制代码

    其它

    copyFrom()

    1. public void copyFrom(Message o) {
    2. this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
    3. this.what = o.what;
    4. this.arg1 = o.arg1;
    5. this.arg2 = o.arg2;
    6. this.obj = o.obj;
    7. this.replyTo = o.replyTo;
    8. this.sendingUid = o.sendingUid;
    9. this.workSourceUid = o.workSourceUid;
    10. if (o.data != null) {
    11. this.data = (Bundle) o.data.clone();
    12. } else {
    13. this.data = null;
    14. }
    15. }
    16. 复制代码

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

    sendToTarget()

    1. public void sendToTarget() {
    2. target.sendMessage(this);
    3. }
    4. 复制代码

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

    asynchronous(异步)

    1. public boolean isAsynchronous() {
    2. return (flags & FLAG_ASYNCHRONOUS) != 0;
    3. }
    4. 复制代码

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

    1. public void setAsynchronous(boolean async) {
    2. if (async) {
    3. flags |= FLAG_ASYNCHRONOUS;
    4. } else {
    5. flags &= ~FLAG_ASYNCHRONOUS;
    6. }
    7. }
    8. 复制代码

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

    说明:

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

    InUse

    1. /*package*/ boolean isInUse() {
    2. return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
    3. }
    4. 复制代码

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

    1. /*package*/ void markInUse() {
    2. flags |= FLAG_IN_USE;
    3. }
    4. 复制代码

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

  • 相关阅读:
    哈夫曼编码原理
    第五届“传智杯”全国大学生计算机大赛(练习赛) [传智杯 #5 练习赛] 复读
    [C#]WPF 分辨率的无关性的问题
    C/C++运算符超详细讲解(系统性学习day5)
    linux入门学习11
    JVM:(十三)垃圾回收概述
    Vue2电商前台项目——完成Home首页模块业务
    2.8 高收藏率小红书笔记怎么写?试一试这7类方法吧【玩赚小红书】
    Ansible的命令及常用模块详解
    STM32常见符号解释定义(持续更新)
  • 原文地址:https://blog.csdn.net/xiaopangcame/article/details/127876549