目录
Intent定义
Intent是一类特殊的组件,它负责对应用中一次操作的动作及动作相关的数据进行描述,Android则根据此描述,负责找到对应的组件,将Intent传递给调用的组件,并完成组件的调用。
Intent作用
Intent的出现,调用方只需要将自己需要的功能通过Intent进行描述,而不必持有具体组件的引用,这些工作全部由底层的Android Runtime来实现,因此,Intent最大的优点就是完美地实现了调用者与被调用者之间的解耦
Intent组件属性
1.action,要执行的动作
SDK中定义了以下一些标准的动作,系统级别的Intent动作分为两类:活动动作和广播动作,前者用于Activity,后者用于BroadcastReceiver。
几种重要的系统活动动作使用Intent的如下常量表示。
- ACTION_MAIN:主动作,用于启动一个不需要接收数据的Activity(例如从桌面启动的活动);
- ACTION_VIEW:查看内容动作,需要具有输入型参数表示查看的内容;
- ACTION_GET_CONTENT:选择内容的动作,需要输出返回结果;
- ACTION_EDIT:编辑内容的动作,需要具有输入型参数表示查看的内容;
几种重要的系统广播动作使用Intent的如下常量表示。 - ACTION_TIME_TICK:时间打点,每分钟会被发送;
- ACTION_PACKAGE_
:包相关,例如包的安装、下载; - ACTION_BOOT_COMPLETED:系统启动完成;
- ACTION_POWER_CONNECTED:电源连接完成;
- ACTION_POWER_DISCONNECTED:电源连接断开。
当然,开发人员也可以自定义动作(自定义的动作在使用时,需要加上包名作为前缀,如"com.example.project.SHOW_COLOR"),并可定义相应的组件如Activity来处理自定义动作。
2.data,是执行动作要操作的数据
这里的数据指的是Android中指向数据的一个URI
如在联系人应用中,一个指向某联系人的URI可能为:content://contacts /1。对于不同的动作,其URI数据的类型是不同的(可以设置type属性指定特定类型数据),如ACTION_EDIT指定Data为文件URI,打电话为tel:URI,访问网络为http:URI,而由Content Provider提供的数据则为content:URI。不同的Action有不同的data指向,就是Action与data之间要匹配,如果不匹配,就找不到相应的数据。
3.type(数据类型),显式指定Intent的数据类型。
一般Intent的数据类型能够根据数据本身进行判定,但是通过设置这个属性,可以强制采用显式指定的类型而不再进行推导。
4.category(类别),被执行动作的附加信息
例如,ALTERNATIVE_CATEGORY表示当前的Intent是一系列可选动作中的一个,这些动作可以在同一块数据上执行。其他常用的category还有以下几种。
- CATEGORY_BROWSABLE:目标组件可以被浏览器安全调用用来显示页面中链接的内容如图像或电子邮件信息等。
- CATEGORY_HOME:目标Activity是设备启动时第一个启动的Activity。
- CATEGORY_LAUNCHER:目标Activity应该显示在桌面中。
- CATEGORY_PREFERENCE:目标Activity是一个选项面板。
每一个通过startActivity方法发出的隐式Intent都至少有一个category,就是"android.intent.category.DEFAULT"
5.component(组件),指定Intent的目标组件的类名称
通常Android会根据Intent中包含的其他属性的信息,比如action、data/type、category进行查找,最终找到一个与之匹配的目标组件。但是,如果直接指定component属性,将直接使用它指定的组件,而不再执行上述查找过程。指定了这个属性以后,Intent的其他所有属性都是可选的。
对于指定了component属性的Intent,因为它明确指定了目标组件的类名称,称为显式Intent;反之,称为隐式Intent
6.extras(附加信息),包含所有附加信息的集合
使用extras可以为组件提供扩展信息,比如,如果要执行“发送电子邮件”这个动作,可以将电子邮件的标题、正文等保存在extras里,传给电子邮件发送组件。
7.flag,该属性用于通知系统如何启动目标 Activity,或者启动之后采取怎样的操作。常见flag如下。
FLAG_ACTIVITY_NEW_TASK:通知系统将目标Activity作为新的Task进行初始化。
FLAG_ACTIVITY_NO_HISTORY:通知系统不要将action放入历史栈中。
注意
如果Intent对象可以通过多个Activity或者Service的过滤器,则用户需要选择执行的组件。如果没有任何匹配,则报告异常。
Intent分类
- 显式Intent
指定了一个组件(通过setComponent(ComponentName)或者setClass(Context,Class)方法),它提供了一个明确的将运行的类,这样的Intent叫显式Intent。通常这种Intent都不再包含其他任何属性信息。显式Intent一般用于应用程序内部传递消息 - 隐式Intent
因为开发人员往往是不知道别的应用程序的组件名称的,没有指定明确的组件名称的Intent,则称为隐式Intent。隐式Intent 无法指定明确的组件名,那如何找到目标组件呢?这个时候就需要用到 IntentFilter 了。IntentFilter负责过滤组件无法响应的Intent,只将自己关心的intent接受进来处理。
Intent传递数据
Intent的定义可知,Intent除了定位目标组件外,另外一个职责就是传递数据信息。
Intent间传送数据一般有两种常用的办法:一种是通过data属性,另一种是通过extra属性。data属性是一种URL,它可以是指向我们熟悉的HTTP,FTP等网络地址,也可以指向ContentProvider提供的资源。通过调用Intent的setData方法可以放入数据,相应地,调用getData方法可以取出数据。
例如,需要启动Android内置的浏览器,利用下面的代码便可将网址信息通过data属性传递给它。
Intent intent=new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.google.com.hk"));
startActivity(intent);
startActivity(intent);
由于data属性只能传递数据的URL地址,如果需要传递一些数据对象,则就需要利用extra了。
extra可以通过Intent的putExtra方法放入数据,方法putExtra的参数是一个Bundle对象。
Bundle是专门用来在Android的应用组件之间传递数据的一种对象,它本质上是一个Map对象,可以将各种基本类型的数据保存在Bundle类中打包传输
IntentFilter
Activity、Service、BroadcastReceiver等组件为了告知Android能够处理哪些隐式Intent,它们可以有一个或多个Intent Filter。每个Intent Filter描述组件的一种能力,即能够接收的一组Intent。实际上,Intent Filter只是筛掉不想要的Intent,也仅仅是不想要的隐式Intent,因为显式Intent总是能够传递到它的目标组件,不管它包含Intent Filter与否。但对于隐式Intent,仅当它能够通过组件的Intent Filter之一才能够传递给它。Intent Filter通常不在实现组件的Java代码中设置,而是在Android应用程序的清单文件(AndroidManifest.xml)中以
● 0~N个
● 0~N个
● 0~N个子元素。
注意:
launchMode为singleTask的时候,通过Intent启动一个Activity,如果系统已经存在一个实例,系统就会将请求发送到这个实例上,但这时,系统就不会再调用通常处理请求数据的onCreate()方法,而是调用onNewIntent()方法,获取intent附加信息的代码放在onCreate()方法中,在Activity的launchMode为singleTask的时候是不适用的。那么此时将相关代码放在onNewIntent中是不是就万事大吉呢?不然,不要忘记,系统可能会随时杀掉后台运行的Activity,如果这一切发生,当收到Intent调用时,那么系统就会新建一个Activity组件实例并调用onCreate()方法,而不调用onNewIntent方法,因此,最安全的方法便是在onCreate和onNewIntent方法中调用同一个处理数据的方法
IntentFilter解析
Intent解析机制主要是通过查找已注册在AndroidManifest.xml中的所有Intent Filter及其中定义的Intent属性,最终找到匹配的Intent。在这个解析过程中,Android是通过Intent的action、data、category这三个属性来进行判断的,判断流程如下。
(1)action检查
IntentFilter可以有0~N个Action用于过滤,一个Intent对象最多只能包含一个Action属性。如果IntentFilter没有设置Action值,所有的Intent都会被通过,如果Intent指明定了action,只要匹配IntentFilter其中一个action即可,否则不能匹配。
(2)category检查
IntentFilter中也可以设置0~N个category,一个Intent对象可包含多个Category属性,也可调用Intent的addCategory(String str)方法来为Intent添加Category属性,只有当Intent中所有的category都能匹配到IntentFilter中的category时才能通过检查。
原则上讲,无论过滤器中如何定义,没有定义种类的Intent总是可以通过该项测试。但是,有一个例外。Android对于所有通过startActivity()方法传递的隐式Intent默认其包含一个种类:“android.intent.category.DEFAULT”(“CATEGORY_DEFAULT”常量)。因此,接收隐式Intent的Activity必须在过滤器中包含“android.intent.category.DEFAULT”
(3)data检测
IntentFilter中能有0~N个Data。Data包含的内容主要是URL和数据类型,在对Data进行检查时主要也是针对这两点进行比较。如下所示:
每个元素指定数据类型(mimeType)和一个URI。URI有四个属性scheme、host、port和path,对应于URI的格式如下:
scheme://host:port/path
例如,下面的URI:
content://com.demo.service:200/folder/1
其中,scheme是content,host是com.example.service,port是200,path是folder/1。其中host和port一起构成URI的凭据(authority),如果host没有指定,port也被忽略。这四个属性都是可选的,但它们之间并不都是完全独立的。例如,要让authority有意义,scheme必须也要指定。要让path有意义,scheme和authority也都必须要指定。
当比较Intent对象和Intent Filter的URI时,以Intent Filter的URI属性为基础,看Intent对象的data属性的uri是否匹配。例如,如果一个Intent Filter仅指定了scheme,所有有此scheme的URI都匹配;如果一个Intent Filter指定了scheme和authority,但没有指定path,所有匹配scheme和authority的URI都通过检测,而不管它们的path;如果四个属性都指定了,要全部匹配才能算是匹配。当然,Intent Filter中的path一般通过包含通配符来要求匹配path中的一部分。
元素的mimeType属性指定数据的MIME类型。Intent对象和Intent Filter都可以用“”通配符匹配子类型字段,例如,“text/”,“audio/*”表示任何子类型。
data检测既要检测URI,也要检测mimeType,规则如下。
- 一个Intent对象既不包含URI,也不包含mimeType:仅当Intent Filter也不指定任何URI和mimeType时,才不能通过检测;否则都能通过。
- 一个Intent对象包含URI,但不包含mimeType:仅当Intent Filter也不指定mimeType,同时它们的URI匹配,才能通过检测。例如,mailto:和tel:都不指定实际数据。
- 一个Intent对象包含mimeType,但不包含URI:仅当Intent Filter也只包含mimeType且与Intent相同,才通过检测。
- 一个Intent对象既包含URI,也包含mimeType(或mimeType能够从URI推断出):mimeType部分,只有与Intent Filter中之一匹配才算通过;URI部分,Intent对象的URI要出现在Intent Filter中,或者它有content:或file: URI,又或者Intent Filter没有指定URI。
注意
这种情形下有个特例,即如果Intent Filter仅列出了mimeType,则Android假定Intent Filter支持URI为content:和file:。因此,如果Intent对象的URI为content:或file:,
而Intent Filter仅包含mimeType,即使没有包含任何URI,Andoid根据此项规则也会允许Intent对象通过检测。