上一篇主要分析Handler家族的四大成员 Looper,Looper 主要功能是从消息队列取消息然后交给Handler 的 handleMessage 函数去处理 Message。这一篇主要讲述 Handler 家族的四大成员Message。
Message----负责保存消息数据,规范消息传递格式
Message是消息传递的载体
1、Message操作数据属性和方法
public int what; 自定义的消息标识,用于区分不同的消息。 public int arg1; 传递的消息只有少量的 integer 型数据。 public int arg2; 同arg1。 public Object obj; 自定义类型的传输数据。
若上面的属性不能满足需求,可使用Bundle。
Bundle data; // 获取 Bundle 数据 public Bundle getData() { if (data == null) { data = new Bundle(); } return data; } // 获取 Bundle 数据,若 data 是空的就返回 null public Bundle peekData() { return data; } // 设置 Bundle 数据 public void setData(Bundle data) { this.data = data; } //使用方法 Bundle bundle = new Bundle(); bundle.putString("String","ABC"); bundle.putFloat("float",0.3f); Message msg = Message.obtain(); msg.setData(bundle);
2、Message的创建
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}). */ public Message() { }
Message构造函数为空,推荐的方式是调用静态方法 Message.obtain()
方法获取 Message
对象。因为 obtain()
方法内部维持了一个链表形式的Message对象缓存池,可从对象池中获取已创建的 Message
对象,而不是每次都创建新的对象,这样可以减少内存分配的开销,提高性能。
数据结构中链表的一个单元包含当前单元的值 (head)和下一个单元的地址指针(next),如果下一个单元不存在那么 next 就是 null 的。那么Message 对象链表式缓存池就需要额外的两个 Message 类型的引用,head 和 next分别对应sPool和next。上图和代码:
int flags; //用于标识当前对象是否存在于缓存池,0 代表不在缓存池中 // 若 Message 对象被存入了 MessageQueue 消息队列排队等待 Looper 处理或者被回收到缓存池中等待重复利用时,那么它就是 in use(正在使用)状态。 仅在 new Message()和 Message.obtain()时候才可清除掉 flags 上的 in use 状态,但不可让处于 in use 状态的 Message 对象去传递消息。 1 << 0确保标志位只占用一个比特位。 static final int FLAG_IN_USE = 1 << 0; //供flags 使用,in use(正在使用)状态 // 通过 synchronized (sPoolSync)让它作为线程并发操作时的锁,确保同一时刻只有一个线程可以访问当前对象的引用 private static final Object sPoolSync = new Object(); // 当前链表缓存池的入口,装载着缓存池中第一个可用的对象 private static Message sPool; // 链表缓存池中指向下一个对象引用的 next 指针 Message next; // 当前链表缓存池中对象的数量 private static int sPoolSize = 0; //从缓存池中取出 Message 对象,可避免分配新对象。 public static Message obtain() { synchronized (sPoolSync) { // pool 不等于空就说明缓存池中还有可用的对象,直接取出来 if (sPool != null) { // 声明一个 Message 引用指向缓存池中的 pool 对象 Message m = sPool; // 让缓存池中 pool 引用指向它的 next 引用的对象 sPool = m.next; // 因为该对象已经从缓存池中被取出,所以将 next 指针置空 m.next = null; // 将从缓存池中取出的对象的 flags 的 in use 标识清除掉 m.flags = 0; // 缓存池中 Message 对象数量减去一个 sPoolSize--; return m; } } // 如果缓存池中没有可用的对象就 new 一个吧 return new Message(); }
包含 Handler 参数的 obtain()方法
Handler target; public static Message obtain(Handler h) { Message m = obtain(); m.target = h; return m; } public void setTarget(Handler target) { this.target = target; } public Handler getTarget() { return target; }
包含 Runnable 参数的 obtain()方法
Runnable callback; public static Message obtain(Handler h, Runnable callback) { Message m = obtain(); m.target = h; m.callback = callback; return m; } public Runnable getCallback() { return callback; }
在 Looper 分发消息时如果 Runnable callback 不为空,则不调用 Handler 的 handleMessage (Message msg)方法,直接运行这个 Runnable callback,即 Runnable 会运行在 Handler 被创建的线程上。
包含其他参数的 obtain()方法
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; }
3、Message的回收
采用 public void recycle()方法用于回收 Message 对象 private static final int MAX_POOL_SIZE = 50; // 缓存池最大存储值 // 区分当前 Android 版本是否大于或等于 LOLLIPOP(Andorid 5.0) 版本的全局静态变量,默认初始值为 true private static boolean gCheckRecycle = true; /** * 用于区分当前 Android 版本是否大于或者等于 LOLLIPOP(Andorid 5.0) 版本 * 内部隐藏方法,在 APP 启动时就会执行该方法,开发者是不可见的 * @hide */ public static void updateCheckRecycle(int targetSdkVersion) { if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) { gCheckRecycle = false; } } //判断当前对象的 flags 是否为 in-use 状态 boolean isInUse() { return ((flags & FLAG_IN_USE) == FLAG_IN_USE); } /** * 调用这个方法后,当前对象就会被回收入缓存池中。 * in-use 状态的不可回收 */ public void recycle() { // 判断当前对象是否为 in-use 状态 if (isInUse()) { // 如果当前版本大于或者等于 LOLLIPOP 则抛出异常 if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } // 如果当前版本小于 LOLLIPOP 什么也不干直接结束方法 return; } // 回收 Message 对象 recycleUnchecked(); } /** * 回收一个可能是 in use 状态的 Message 对象 * 在 MessageQueue 和 Looper 内部处理排队 Message 时也会使用这个方法 */ void recycleUnchecked() { // 将当前 Message 对象置为 in-use 状态 flags = FLAG_IN_USE; // 将当前 Message 对象的所有数据属性置为默认状态 what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { // 如果当前缓存池对象中的数量小于缓存池最大存储值(50)就存入缓存池中 if (sPoolSize < MAX_POOL_SIZE) { next = sPool; //存入缓存池 sPool = this; sPoolSize++; //缓存池数量加 1 } } }
在 LOLLIPOP(Andorid 5.0) 版本之前,强制使用 recycle()方法回收 in-use Message,也会被回收。
4、Message的其他用法:
4.1、序列化:
Message 支持对象的序列化,即可把对象转为字节形式,可以保存到本地也、可以用于网络传输。需实现 Parcelable 接口,实例化 Parcelable.Creator 接口,并重写 describeContents()和 writeToParcel()方法。
// 实现 Parcelable 接口 public final class Message implements Parcelable{ ... // 实例化 Parcelable.Creator 接口,完成 Parcel 对象转 Message 对象的操作 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]; } }; // 序列化对象时的特殊种类对象描述,这里开发人员没有修改,就是默认的 0 public int describeContents() { return 0; } // 重写Parcelable接口的writeToParcel方法,将Message对象转为Parcel对象, public void writeToParcel(Parcel dest, int flags) { // 以下代码均是将 Message 对象中的属性写入 Parcel 对象中 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); } // 从 Parcel 对象中读取数据转为当前对象的属性 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(); } }
4. 2、异步设置
Looper将消息队列 MessageQueue 中的消息依次取出后分发,每个消息传输都是有时间顺序的,可控的。将消息设置成异步传输后, Message 对象将不再受 Looper 的控制,传输的顺序可能会被打断,不可控。
// 该常量代表为异步传输方式 static final int FLAG_ASYNCHRONOUS = 1 << 1; // 判断是否为异步传输 public boolean isAsynchronous() { return (flags & FLAG_ASYNCHRONOUS) != 0; } // 设置当前对象是否为异步传输 public void setAsynchronous(boolean async) { if (async) { flags |= FLAG_ASYNCHRONOUS; } else { flags &= ~FLAG_ASYNCHRONOUS; } }