Android Framework-Handler消息机制(三)

         上一篇主要分析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;
    }
}
  • 24
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值