Intent作为Android的消息对象,让组件间的通信更加方便,其中主要有3个使用情景,启动Activity,启动service,发送broadcast。这样的设计有两大好处:
应用程序的开发完全面向组件编程,具体到代码层面,应用开发者只要在组件的生命周期相关的回调函数里实现应用程序的逻辑,然后设计好组件间的通信关系即可,这样的设计大大降低了应用开发的难度。
代码可高度复用,比如你做一个社交的App,需要拍照的功能,你只需要给系统发送一个“我要拍照”的消息,你就可以调用系统或者第三方的拍照应用,来实现拍照功能,并不需要自己重新编码。
显式Intent和隐式Intent
Android有两种类型的Intent:
显式(explicit)Intent
显式 Intent直接指定了要启动的组件名字。隐式(implicit)Intent
隐式Intent没有指定要启动的组件的名字,而是声明了要执行的动作,Android系统会根据你提供的动作,来寻找合适的,能够处理该动作的组件。
Intent的重要成员域
ComponentName mComponent
要启动的组件的名字。如果该域不为空,它指定了组件的名字,那么该Intent就是显式的,组件的名字是由类名和包名组成的。如果该域为空,那么该Intent就是隐式的。
String mAction
指定了要执行的动作。Android已经定义了很多常见的Intent,这里面定义很多常见的动作。如果需要自定义动作,最好包含包名,例子如下。
要执行的动作很大程度上决定了Intent其它成员的结构组成,特别是Extras和Data成员。
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
Uri mData
指定了要执行动作的数据,通常也需要指定数据的MIME type。String mType
指定了要执行的动作的数据类型(MIME type)。ArraySet<String> mCategories
指定了处理事件的组件的类别。一个Intent可以在指定多个类别,但是通常大部分Intent不需要指定类别。有两个类别比较常见:CATEGORY_BROWSABLE
指定该类别的Activity允许被浏览器启动去显示数据(如一张图片,一封email)。CATEGORY_LAUNCHER
指定该类别的Activity是任务栈中的首个Activity,Activity会出现在launcher App页面上,用户可以点击启动。
Bundle mExtras
存放附加信息的键值对int mFlags
标志信息指示Android系统启动一个Activity的方式以及启动后如何处理他
Intent解析
我们知道,显式Intent是指名道姓的,Android系统收到了后,直接路由给指定的组件即可。但是隐式的Intent则不同,Android收到这种消息后,需要经过一串的匹配测试的过程来找出合适的组件,这个测试的过程就是Intent的解析。
我们经常可以看到应用的AndroidManifest.xml
会定义类似如下结构的<intent-filter>
,这个结构定义了该组件能够处理的Intent。每个组件可以定义0个或者多个<intent-filter>
,每个<intent-filter>
可以定义0个或者多个action,category,data。
<activity android:name="ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
当你需要执行某动作时,你在代码中new了一个Intent,给Intent设置了相关的action,data/type,category后发送给Android系统,Android系统收到Intent后会枚举系统中所有的组件,包括系统应用,第三方应用,测试组件的IntentFilter和Intent是否匹配,通过了测试Android系统就会把该Intent发送给对应的组件。
测试IntentFilter和Intent是否匹配,需要测试Intent中Action,Category,Data/Type,只有三个测试都通过了,该Intent才会发送给IntentFilter所在的组件。如上面提到的,一个组件的可以定义多个IntentFilter,只要Intent与其中的一个IntentFilter匹配,那么也认为该组件是可以处理该Intent的。
Action测试
Intent指定Action个数 Fillter定义Action个数 测试结果 0 0 通过 0 1/多个 通过 1/多个 0 不通过 1 1/多个 子集通过,非子集不通过 从上表可以看出,要通过Action测试,Intent指定的Action必须要在Fillter中也有指定才行。
Category测试
Intent指定Category个数 Fillter定义Category个数 测试结果 0 0/1/多个 通过 1/多 0 不通过 1/多 1/多 子集通过,非子集不通过 从上表可以看出,要通过Category测试,Intent指定的Category必须要在Fillter中也有指定才行。
注意:
使用startActivity或者startActivityForResult启动Activity组件时,Android会自动给Intent加上CATEGORY_DEFAULT类别,如果你希望你的Activity接受隐式Intent的话,需要给你的Activity的<intent-filter>
加上”android.intent.category.DEFAULT”类别。Data测试
<intent-filter>
中的<data>
元素可以指定一个URI结构和MIME type,每个URI有四个独立的属性,<scheme>://<host>:<port>/<path>
,这四个属性都是可选的,但是是线性依赖的:如果scheme没有指定的话,host可以忽略
如果host没有指定的话,port可以忽略
如果scheme和host都没有指定的话,path可以忽略
如果比较Intent中的URI和filter中的URI的话,只要比较filter中URI指定了的属性部分即可,也就是说,Intent指定的URI如果是filter指定的URI的更具体的URI时,也是匹配的,具体描述如下:
如果filter只指定了scheme(假设为S1)的话,Intent中的URI的scheme为S1的Intent都匹配该filter
如果filter只指定了scheme(假设为S1)和authority(假设为A1),没有指定path的话,Intent中的URI的scheme为S1,authority为A1的Intent都匹配该filter,不管Intent中的URI的path是什么
如果filter指定了scheme(假设为S1),authority(假设为A1),path(假设为P1)的话,Intent中的URI的scheme为S1,authority为A1,path为P1的Intent都匹配该filter
Data测试会同时比较URI和MIME type,规则如下:
如果Intent既没有指定URI,也没有指定MIME type的话,如果要通过
Data测试的话,filter既不能指定URI,也不能指定MIME type如果Intent指定了URI,但没有指定MIME type的话,如果要通过
Data测试的话,Intent所指定的URI要匹配filter的URI,同时filter没有指定MIME type如果Intent指定了MIME type,没有指定URI,如果要通过
Data测试的话,filter要指定和Intent一样的MIME type,同时不能指定URI如果Intent指定了URI和MIME type,如果要通过MIME type部分的测试的话,Intent所指定的MIME type必须匹配filter的。如果要通过URI部分的测试的话,要么他们的URI匹配,要么Intent的URI是content: 或者 file:的URI,同时filter没有指定URI。换句话说,组件默认支持content: 和file:数据,如果组件的filter只列出了MIME type的话。