4.4Message深度理解
目录介绍
- 01.先来了解下Message
- 02.Message是做什么用
- 03.Message如何创建
- 03.看一下几个成员字段
- 04.Message.obtain()分析
- 05.Message的命令模式
01.先来了解下Message
- Android应用是事件驱动的
- 也可以说是Message来驱动的。每个进程中都有一个默认的消息队列MessageQueue,其维护了一个待处理的消息列表,Looper不断地从中取出消息、处理消息。
- 如何处理Message创建和销毁
- 此时我们不禁会抱有一个疑问,在应用运行期间,系统岂不是会不断地创建Message、处理Message、销毁Message?
- 答案当然是否定的,Android作为一个成熟的系统平台,自然不会轻易地采用大量重复构建这种低效耗能的运作方式,本篇文章我们就来了解一下Message。
02.Message是做什么用
- Message的作用
- 大概意思是,Message类被用于发送给Handler,其可以存储一些描述信息和任意数据类型的对象。
- Message有两个额外的int字段和一个额外object字段,可以满足很多情况下的需求配置。
- 虽然Message的构造方法是public的,但是创建Message最好的方法还是去调用Message.obtain()方法或者调用一系列Handler.obtainMessage()的方法,这些方法会从一个回收池中去获取Message对象。
- 复用对象池
- Android的消息机制并不会大量重复创建Message对象,而是重复利用在消息池中已存在的Message对象。
- 从Handler类源码中我们可以看到,Handler中所有需要构建Message对象的地方,包括一系列Handler.obtainMessage()的方法,其实质上都是通过Message.obtain来获得的。
03.Message如何创建
- 创建Message对象的几种方式:
- Message msg = new Message();
- Message msg = Message.obtain();
- Message msg = handler1.obtainMessage();
- 几种方法对比
- 后两种方法都是从整个Message池中返回一个新的Message实例,能有效避免重复Message创建对象,因此更鼓励这种方式创建Message
- Message核心字段含义
m.what = 定义常量,用来发送消息和匹配消息的数值; m.target = 表示这个消息是从那个handler发出来的 m.arg1 = orig.arg1; m.arg2 = orig.arg2; m.obj = 赋值的对象; m.replyTo = orig.replyTo; m.sendingUid = orig.sendingUid;
03.看一下几个成员字段
- 代码如下所示
// sometimes we store linked lists of these things /*package*/ 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;
- 先来看看Message类中几个重要的字段
- next字段的注释我们可以发现Message的消息池实质上就是个链表,而这个next指向下一个Message对象;
- sPoolSync是一个普通Object对象,用于在获取Message对象时进行同步锁;
- mPool字段就是一个Message对象,mPool是链表中的第一个Message对象;
- sPoolSize字段表示的是消息池已有Message的数量;MAX_POOL_SIZE表示消息池的最大容纳数量。
04.Message.obtain()分析
- 看看Message.obtain()方法
- 如果消息链中没有消息时,就会new一个新的Message对象并返回;如果消息链中有可用对象,就会获取到当前链表的mPool,而后将mPool指向下一个Message对象。
/** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }
- 当链表中有可用对象时,obtain方法是从链表中获取Message对象,但是没有对象时,obtain方法中创建的Message又是怎么添加到消息池中的呢?说到这里,不知大家还记不记得在Looper.loop方法中,每一条Message被处理后最终都会调用Message.recycleUnchecked()方法,没错,答案就在这个方法里,我们来看看Message.recycleUnchecked()方法的源码。
/** * Recycles a Message that may be in-use. * Used internally by the MessageQueue and Looper when disposing of queued Messages. */ void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
- 从代码中可以看出
- recycleUnchecked()方法有两个作用:首先,清空消息状态,同时设置flags = FLAG_IN_USE,表明该消息已被使用,这个flags在obtain()方法中会被设置为0;
- 然后,如果消息池的大小未超过最大容纳数量,就将自身添加到链表的表头。也就是说,Message并不是在创建时而是在回收时被添加到消息池中的。
05.Message的命令模式
- Android消息机制实质上是一个非典型的命令模式,所以这里简单说一下命令模式。
- 定义:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
- 命令模式中主要有三种角色:
- Receiver:接收者角色,命令的最终执行者。
- Command:命令角色,需要执行的所有命令都是在这里声明的。
- Invoker:调用者角色,接受命令并执行命令。
- 命令模式的优点:
- 类间解耦,将调用者角色与接收者角色实现解耦,调用者实现功能时只需调用Command抽象类的execute方法就可以,不需要了解到底是哪个接收者执行;可扩展性好,Command的子类可以非常容易地扩展,而调用者Invoker和高层次的模块Client不产生严重的代码耦合。
- 命令模式的缺点:
- 命令模式主要的缺点就是Command类膨胀的问题,如果有N个命令,Command的子类不是几个而是N个。
- 使用场景:
- 需要抽象出待执行的动作,然后以参数的形式提供出来——类似于过程设计中的回调机制,而命令模式正是回调机制的一个面向对象的替代品。
- 在不同的时刻指定、排列和执行请求。一个命令对象可以有与初始请求无关的生存期。
- 需要支持取消操作。
- 支持修改日志功能,这样当系统崩溃时,这些修改可以被重做一遍。
- 需要支持事务操作。
- 回到Android消息机制,上边说到Android消息机制是一个非典型的命令模式,其分工如下:
- 接收者:Handler,执行消息处理操作;
- 调用者:Looper,调用消息的的处理方法;
- 命令角色:Message,消息类。
- 为什么Android消息机制为什么会使用命令模式了。
- 不难发现,Android消息机制的接收者和调用者之间是存在依赖的,似乎与命令模式相违和,但仔细一想倒也无可厚非,设计模式的存在意义就是为了使代码的更易于维护和扩展。
- 之前说过接收者和调用者实现解耦,其最大的好处就是让未知对象之间的调用关系更加灵活,而Android消息机制中的接收者、调用者和命令角色三者角色固定而明确,自然也不必需要这个扩展性,命令角色的单一还解决了命令模式的类膨胀问题。