首页 > 代码库 > Intent的那些些事儿
Intent的那些些事儿
请原谅我用这么文艺的标题来阐释一颗无时无刻奔腾着的2B青年的心。可是今天要介绍的Intent绝不2B,甚至在我看来,或许还有些许飘逸的味道,至于飘逸在哪里呢?那我们就好好来剖析剖析Intent和它的好搭档IntentFilter的基友情。
开场告白
Intent在Android大家庭中是一个活泼的小男孩,从小就是交际草。在代码中,Activity、Service、BroadcastReceiver这三个重要的大妈级重量组件,之间的调用关联都是依靠Intent去交流的,例如Activity的startActivity(),Service的startService()、bindService(),BroadcastReceiver的sendBroadcast()、sendOrderBroadcast()等,其中都是需要携带一个Intent(或IntentFilter)的参数。
当然Intent中要指定要启动组件的信息,或明显活隐晦。明显的就是显式启动:举个例子来说就是Intent你去把这东西交给某某(名字)Activity大妈。隐晦的是隐式启动:当然得不免俗得给例子就是Intent你把这个东东交给昨晚和我一起在菜市场跳广场舞的那个胖大妈,其中广场舞和胖就把某大妈给赤果果的定位了。这就是Intent的两种匹配方式,我都觉得我自己说的怪抽象的了。
接下来就是介绍IntentFilter的好时机了,它就是大妈的儿子(组件的拦截器)。当如果Intent隐式启动时,Intent会找他的一群小伙伴IntentFilter来,然后问他们说,你们谁的妈昨晚去菜市场跳广场舞了?你们谁的妈又好胖好胖的?然后IntentFilter各想各妈,哎呀,其中一个IntentFilter拍拍脑袋说,这不就是我妈吗!然后他就屁颠屁颠的领着Intent去找某Activity妈把东东交给她了。
为了避免大家看不懂上面的例子,我还是发扬一下2B青年的社会责任感,用比较教条的语言来介绍一下Intent和IntentFilter:Intent是一个意图对象,组件可以通过设定Intent来启动另外一个组件,有显式启动和隐式启动两种方法,显式启动直接指定要启动的组件的名字,隐式启动则指定action、category、data等来启动符合条件的组件;其中的IntnetFilter就是一个意图过滤器,它会设定一些过滤规则如action、category、data等,如果Intent中的action、category、data与过滤器中相匹配,则为启动该过滤器的组件。
码点代码
攻城狮的思维就是不见代码不给赞,所以我还是得老老实实得码几个代码出来撑撑场子。但是Intent的显式启动和隐式启动代码度娘一下几大页,在这里再贴上来就对不起人民群众了。所以呢,我就记录点关键代码,如果后面犯失忆了也可以回来侦查一下容易忘的知识。
显式启动Activity、Service:
Intent intent1 = new Intent(getApplicationContext(), MyActivity.class);startActivity(intent1);//启动Activity组件startActivityForResult(intent1, 1);//启动有返回的Activity
Intent intent2 = new Intent(getApplicationContext(), MyService.class);startService(intent2);//启动ServicebindService(intent2, conn, 1);//绑定Service
隐式启动Activity、Service:
Intent intent1 = new Intent();intent1.setAction("com.net168.test.action.myactivity");startActivity(intent1);//启动Activity组件Intent intent2 = new Intent(); intent1.setAction("com.net168.test.action.myservice");startService(intent2);//启动ServicebindService(intent2, conn, 1);//绑定Service
发送广播:
Intent intent = new Intent();intent.setAction("com.net168.test.action.mybroadcast");sendBroadcast(intent);//发送普通广播sendOrderedBroadcast(intent, null);//发送有序广播
其实很简单,简单得我这的不好意思介绍了。。。
Intent的家世于IntentFilter的纠结
Intent的背景很复杂,复杂到比一些不入流的HEI社会还复杂些。大体来说,Intent大致包含了Component、Action、Categroy、Data、Type、Extra和Flag这七大金刚(属性),后面我们就好好解释这些大哥大姐们。
Component:它需要接受一个ComponentName的对象,创建一个ComponentName需要指定包名和类名--这就唯一确定了一个组件类,这样程序就可以通过给定的组件类去启动相应的组件;所以我们可以从这里重新给显式启动和隐式启动分个类,一般如果明确指定ComponentName时就是显式启动,反之就是隐式启动。另外我们可以直接在实例化Intent时在构造函数中传入上下文和类名也可以直接帮我们设定了ComponentName,如Intent(this,MyActivity.class),推荐这种方式,比较简洁。
Action:Action要完成一个抽象的动作,但具体由哪个组件来完成则不确定,需要根据IntentFilter来确定组件;在Intent中,Action最多只能有1个,而在IntentFilter中Action可以有多个。这是隐式启动一个组件最基础的属性。
Category:它为Action增加额外的类别信息,因为Action可以启动到多个组件,但是如果往下更详细的分析要启动的组件,可以通过Category来进行拦截;在IntentFilter和Intent中Category可以有多个。任何一个IntentFilter的默认存在一个Intent.CATEGORY_DEFAULT的属性。
Data、Type:这两个算是比较亲的属性。
Data属性通常用于向Action提供操作的数据,其值为一个Uri对象,Uri对象满足 scheme://host:port/path 的格式,如 content://com.android.contacts/contacts/1,其中content是scheme的部分、com.android.contacts是host的部分、port部分被省略、contacts/1是path的部分。
Type属性用于指定该Data所指定Uri对应的MIME类型,可以是所以自定义的类型,只要符合abc/xyz格子的字符串即可。
在使用中,我们要知道这两个属性会相互覆盖:如果Intent先设置Data属性,后设置的Type属性将会把Data属性覆盖掉,反之同理。所以如果要设置Data和Type属性的话需要调用Intent的setDataAndType()的方法。
Extra:Extra是用于多量的数据交换,它的对象是一个Bundle对象。
Flag:Flag属性是为了该Intent添加一些额外的控制旗标,可以通过addFlag()添加控制旗标,通常可以通过它来控制Activity的启动效果和启动方式。
这七个属性中,Intent与IntnetFilter匹配的匹配规则在在于Action、Category、data、Type这几个属性中,具体规则如下:
Action的匹配规则:
1、如果Intent中包含action_A,而在IntentFilter中只要存在action_A这个字符串,无论是否还有其他的action_B、action_C,这个Intent都能被IntnetFilter成功匹配。
2、如果如果Intent中包含action_A,只要IntnetFilter中不存在action_A的话,IntentFilter就不能匹配到Intnet。
3、如果Intent中不存在任何action_x,只要IntnetFilter中存在任意一个action_x的话,Filter就能匹配上Intent。
4、如果IntentFilter不存在任何action_x,则无论Intent中无论存在任何action_x或者没有任何action_x的话,两者是完全不能匹配上的。
Category的匹配规则:
这个匹配关系较为简单,只要Intent中所有category在IntentFilter中都存在,则Intent可以匹配上IntnetFilter,不论此时Intentfilter中还存在其它的category。举个例子,如果Intent中存在category_A、category_B,而Intentfilter中存在category_A、category_B、category_C,则可以匹配;如果而Intentfilter中存在category_A、category_D、category_C,因为Intentf中不存在Intent中存在的category_B,所以匹配失败。
Data和Type的匹配规则:
data由URI来描述和定位,URI由三部分组成,scheme://host:port/path 模式://主机:端口/路径
此外在事件中,还可以设置data的MIME类型,作为事件的datatype属性。
首先明确一个匹配原则,就是对于URI的匹配,只比较IntentFilter中声明的部分。
部分匹配原则:只要IntentFilter中声明的部分匹配成功,就认为整个URI匹配成功。
举例来说, content://com.silenceburn.SdCardTester:1000/mydata/private/
和IntentFilter定义为 content://com.silenceburn.SdCardTester:1000/ 是可以匹配的。
注意IntentFilter中并没有定义path部分,但是依然可以匹配成功,因为IntentFilter不声明的部分不进行比较。
换句话讲,任何符合content://com.silenceburn.SdCardTester:1000/的事件,无论path是什么,都可以匹配成功。
接下来是真正的data部分的,也就是URI的匹配规则如下:
1. 如果data的URI和datatype为空,则 filter 的URI和type也必须为空,才能匹配成功
2. 如果data的URI不为空,但是datatype为空,则 filter 必须定义URI并匹配成功,且type为空,才能匹配成功
3. 如果data的URI为空,但是datatype不为空,则 filter 必须URI为空,定义type并匹配成功,才能匹配成功
4. 如果data的URI和data都不为空,则 filter 的URI和type都必须定义并匹配成功,才能匹配成功。对于URI部分,有一个特殊处理,就是即使IntentFilter没有定义URI,content和file两种URI也作为既存的URI存在。
Intent的那些些事儿