Android的Intent机制
Intent对象的作用和构成
android意图机制最核心的设计思想,就是引入了组件管理服务作为连接组件的管理者。 该服务的作用:
- 通过组件的配置信息了解系统中每个组件的类别和功能
- 帮助调用组件寻找符合其需求的实现组件,将调用者与实现在彻底解耦
在整个意图机制下,有三方角色参与组件间的连接和通信
【调用组件】连接请求的发起者,它期望寻找其他组件来帮助完成所需的功能。通过调用startActivity和bindService等函数发起对目标组件的连接请求
【实现组件】响应调用者请求完成所需任务的组件,每个组件都可以作为调用者请求第三方帮助,也都可以扮演实现着去完成对应的请求。对于实现组件而已,不需要关注其调用者是谁,只需依照请求者发送的Intent对象去执行相关功能即可
【组件管理服务】在调用组件与实现组件的连接过程中,组件管理服务扮演了调度者的角色。从调用组件中接收到Intent对象,然后将该对象与应用管理服务收集到的组件Intent Filter对象进行比较,从中选择出符合调用组件需求的实现组件,最后构造并调用实现组件对象,组件管理服务是一个系统服务,运行在系统核心进程的独立线程中,通过进程间通信机制,与各组件进行交互
Intent对象的作用
Intent对象是组件间通信信息的载体。它封装了调用组件提供的指令和数据,通过组件管理服务序列化传递给实现组件,实现组件可以利用这些信息完成所需功能。 Intent对象定义了组件间连接协议。每个Intent对象都包含若干个数据项,每个数据项都有其内涵,调用者组件根据这些规范构造Intent对象描述其需求;实现组件根据这些规范解析Intent对象执行所需功能;而组件管理服务依照这些规范寻找与需求匹配的组件,构造连接
Intent对象的构成
Intent类的实现非常简单,只是包含了若干个数据项
- Action项 Action就是用来表达动作的。Action就是一个字符串,可以用setAction函数为intent对象指定一个动作,也可以用getAction读取Intent对象中的动作信息。
- Data项 意愿的表达,比如我吃宫保鸡丁,主语“我”,在意图机制下对应着调用组件,谓语“吃”,用Intent的Action对象来表达,宾语"宫保鸡丁"则对应着Data,setData,setDataAndType来进行设置,通过getData函数来读取。Data数据也是用字符串进行存储的,它的个数符合URI标准,URI具有丰富的表达能力,能够表达存储在任何地方的数据
- Type项 如果Data用于特指,那么Type用于泛指,setType,setDataAndType,getType,Type是MIME格式的字符串数据,用于描述组件能够处理的请求类型,或者补充说明Data数据的类型。可以通过通配符*来表示整个类别的信息 在Intent对象中,Data项和Type项很多时候是互斥使用的。一个Intent中,需要Data表示数据还是用Type表示数据类型通常是和Action密切相关的。
- Category项 category表示约束,每个Intent对象可包含多个Categories。addCategory添加Category项,getCategories获取该Intent对象Category项的集合。同一个Intent中的多个Category项彼此间是"与"关系。也就是说一个组件需要支持全部的Category项才能处理该请求。
- Component项 Component指的是目标组件的类型信息,通过setComponent方法利用类名进行设定,也可以通过setClass方法利用类型对象信息进行设置。当调用组件明确了Component信息,组件管理服务就不再需要根据Action、Data等信息去寻找满足其需求的实现组件了。只需按照Component信息实例化对于的组件作为功能实现者即可。
- Extras项 Extras是Intent中数据传输的载体,负责将数据从调用组件传递到实现组件。Extras是一个Bundle对象,该对象按照键值对的方式存储数据,它实现了Parcelable接口,可以进行数据的序列化和反序列化,从而在进程中传递。
- Flags项 一个整形数,由一系列的标志位汇集而成。它对于实现组件而言完全透明,是调用组件指定组件管理服务构造实现组件的方式,常用于改变实现组件的任务模型和进程模型等。被设定的Flags标志位将会叠加生效。但需要注意的是,很多标志位之间是相关联逻辑关系的,有的标志位需要同时设定,而有的标志位直接则具有一定的互斥性。
Intent对象解析
组件管理服务负责接收和分析Intent对象。Intent对象可以分成两类:精确描述的Intent(Explicit Intent)和模糊描述的Intent(Implicit Intent) 精确描述的Intent,指的是所有带有Component信息的Intent对象。在精确描述的前提下,Intent对象只是作为消息的载体存在 模糊描述的Intent,对基于模糊描述的Intent调用组件而言,它们不在乎实现组件是谁,只要求该组件能够依照Intent对象中描述的意图和需求完成对应的任务。 精确描述的Intent通常用于应用内部的通信,因为调用组件和实现组件彼此了解,不需要动态机制,只需要最快地将消息传递过去就好了。 而模糊描述的Intent,则用于不同应用组件间的互联互通,它的存在将请求者和实现者完全解耦,极大地提升了系统的灵活性。
Intent Filter对象
与Intent相似,IntentFilter对象也包含Action、Type、Data、Category等数据项,每个数据项的结构和含义,与Intent中的数据项也一一对应。当组件管理服务接收到调用组件发送来的基于模糊描述的Intent对象时,会与所有组件的IntentFilter信息进行匹配技术,寻找符合需求的实现组件。 每个组件都可以有任意数量的IntentFilter,组件包含的Intent Filter对象越多,说明它能接受Intent请求的范围越广。而不添加任何Intent Filter对象的组件,仅能通过Intent对象精确地进行调用 组件对于的IntentFilter信息,一般通过配置文件的项进行添加,放在界面组件、服务组件或触发器组件的配置项中。
意图匹配的算法流程
Intent对象和Intent Filter对象的匹配过程,就称为意图匹配算法。
算法输入的是进行比较的Intent对象和Intent Filter对象,输出的是一个32位的整数值,用于表征两者的匹配程度。 整个匹配算法的流程可分为3个步骤 1. Action的比较:每个IntentFilter对象都必须包含Action信息,如果没有,则对任何一个Intent对象都会匹配失败 2. Data和Type的比较:Data和Type信息是Intent Filter中最复杂的数据项,其比较算法是决定Intent与Intent Filter对象匹配程度的关键 3. Category的比较
匹配组件的选择
如果有多个Intent Filter对象与调用组件发出的Intent对象都相匹配,就需要在所有符合条件的Intent Filter对象中进行筛选,选出最符合调用组件和用户需求的实现组件,这个流程就称为匹配组件的选择。 在组件选择上,最核心的机制就是基于优先级的排序。每一个Intent Filter对象都有一个优先级,从-1000到1000,值越大,优先级越高 实例例子,如拦截短信,可以采取提升Intent Filter优先级的策略来实现:
<receiver android:name="third_parth.SmsReceiver">
<intent-filter>
<action android:priority="1000"
android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
在third_party.SmsReceiver这个触发组件中,系统可以定制策略拦截特定的短信,通过调用abortBroadcast()函数终止广播,使其他短信应用的Intent Filter无法匹配短信通知
public void onReceive(Context context,Intent intent)
{
if(IsBlockSms(intent))
abortBroadcast();
}
意图匹配的优化
意图机制中,引入第三方组件管理服务,降低了调用组件和实现组件之间的耦合,提高了系统的灵活性及组件的复用性,但同时,由于第三方服务地介入增加了组件间连接的成本,可能会使组件间的调用不够流畅。因此,系统对组件的匹配和选择过程进行了大量的优化,以提升意图匹配和组件调用的效率。
- 索引:此流程涉及大量的字符串比较,比较耗时。组件管理服务会通过哈希表,为所有Intent Filter对象的Action、Type等数据项建立索引。每个索引对应着一组Action相同,Type相同,或者其他数据项相同的Intent Filter对象。Intent对象会先将其中的数据项与索引项进行比较,快速地选择出可能与Intent相匹配的Intent Filter对象。
- 缓存:将Intent和Intent Filter的匹配结果记录下来,当再碰到相同的Intent的调用时,可直接返回上次记录的结果,从而跳过意图匹配的过程,加速组件的调用。服务组件,组件管理服务会在内存中通过哈希表的形式保留各个Intent对应的服务组件。
意图机制的应用
意图机制在界面组件中的应用
调用组件可以通过调用startActivity和startActivityForResult等函数发起对目标界面组件的调用,选择并构造出一个符合其需求的界面组件实例,切换至前台与用户进行交互。调用组件可以在构造的Intent对象中添加一些Flags标志位信息,用于告知组件管理服务如何去构造实现组件。当多个界面组件都能满足调用组件的需求,就需要从中选择一个最适合的组件作为最终的实现组件。对于界面组件的选择,需要兼顾效率和公平性。
- 效率:选择过程中尽可能流畅
- 公平性:每个符合调用者需求的组件,都应当具有同等的权利来响应调用组件的请求。 首先,通过算法对已排序的Intent Filter进行筛选,组件管理服务会将排序后队列中的第一个Intent Filter对象(记为A)与第二个Intent Filter对象(记作B)进行比较,如果满足一下条件中的一个,组件管理服务就会直接选择A对象对应的组件作为实现组件
- A的优先级高于B
- A包含intent.CATEGORY_DEFAULT,而B没有 同时,Android会将所有符合需求的组件列举起来,让用户决定选择哪个组件来执行该操作。用户可以选择该组件作为执行类似Intent调用的默认组件 如果调用组件期望绕过默认值的约束,让用户根据当前场景再次做出选择,可以通过Intent.createChooser方法自定义组件选择列表 activity.startActivity(Intent.createChooser(intent,R.strng.invite_friend));
意图机制在服务组件中的应用
调用组件可以调用Context.startService函数启动符合需求的服务组件,也可以通过Context.bindService函数绑定对应的服务组件。由于服务组件是在后台提供服务的,当出出现多个匹配项时,无法像界面组件一样构造交互界面让用户来抉择。因此,Android会默认选择优先级最高的组件作为实现组件。 这种情况下,只要备选组件没有变化,对于同一个Intent对象而言,其实现组件是固定的,因此,组件管理服务会记录Intent对象与其实现组件的对照关系,当再次通过相同的Intent对象请求服务时,就可以跳过组件匹配计算,直接返回实现组件。
意图机制在触发器组件中的应用
触发器组件的功能就是监听广播事件的Intent对象,并对其进行简单的解析和处理。调用组件调用sendBroadcast或sendOrderedBroadcast函数进行事件的广播。这些事件消息的载体就是Intent对象 用于广播事件中的Intent对象的Action项,与用于界面组件调用中Intent对象的Action项是完全分离的。 在触发器组件的构造中,不存在匹配组件选择的问题。
小结
Android的意图机制是Android应用模型的核心,解决了组件间的连接问题。通过组件管理服务提供的Intent对象与Intent Filter对象的匹配策略,降低了组件间的耦合度,提供了平台的灵活性,增强了组件的复用性,从而从根本上减轻了应用开发的负担。