Intents and Intent Filters(Intent和Intent过滤器)
2014-04-15 15:47
543 查看
文章转载禁止用于商业用途,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处莫高雷草原以及作者@JiongBull。
访问Android API指南目录索引查看全部翻译
Intent和Intent过滤器
component)的操作。尽管intent有许多方式可以帮助组件之间进行交流,大体上有这三种最基础的用例:
启动activity:
调用startActivityForResult()来启动activity能在它结束时接收返回的结果。你的activity会在
启动service:
如果service是使用客户端-服务器接口设计的,你可以从其他组件传递一个Intent给bindService()来绑定到service上。更多信息,请参阅Services指南。
分发broadcast:
broadcast是一种任何应用都能接收到的消息。系统会为系统事件分发不同的broadcast,比如系统启动时或设备充电时。你可以传递一个Intent给
intent有两种类型:
显式intent 通过名称(全限定类名)来指定要启动的组件。你通常可以使用显式intent来启用你应用里的组件,因为你知道你想启动的activity和service的类名。例如,启动一个响应用户操作的新的activity或者启动一个在后台下载文件的service。
隐式intent 不用指定组件的名称,但是必须声明要执行的操作,这样其他应用的组件就能捕获到它。例如,如果你想在地图上显示用户的位置,你可以使用隐式intent去请求其他满足条件的应用在地图上显示具体的位置。
当你创建了显式intent来启动activity或service时,系统就会立即启动在Intent对象中指定的组件。
图1. 插图显示了隐式intent如何通过系统分发来启动另外的activity:[1] Activity A创建带操作描述的
filter。当有匹配时,[3] 系统通过把Intent传递给匹配的activity(Activity
B)然后调用它的
当你创建了隐式intent时,Android系统就会通过把intent里的内容与设备上其他应用在manifest
file中声明的intent filter进行比较来找出合适的组件启动。如果intent能匹配到一个intent
filter,系统就会启动那个组件,并把Intent对象传递给它。如果匹配了不止一个intent
filter,系统就会显示一个对话框,让用户来挑选使用哪个应用。
intent filter是一种在应用manifest文件里的表达式,它说明了组件想要收到的intent类型。例如,为activity声明intent filter,其他应用就能使用某种确定类型的intent启动你的activity。同样地,如果你没有为activity声明任何intent filter,那么它只能被显式intent启动。
注意:为了保证应用的安全性,在启动Service时请确保使用显式intent,并且不要为你的service声明intent filter。使用隐式intent启动service是有安全风险的,因为你不能确定什么service会响应你的intent,并且用户也不知道哪个service启动了。
Intent中主要包含的信息如下:
组件名称
要启动组件的名称。
这是可选的,但是建议使用显示intent,这意味着该intent只会被分发给组件名称定义的应用组件。如果没有组件名称,那么这个intent就是隐式的,系统会基于intent中的其他信息决定应该把该intent分发给哪个组件(例如下面描述的操作、数据和类别)。所以如果你希望启动应用中某个特定的组件,那么你应该指定组件的名称。
注意:启动
Intent里名称的域变量是
Action表示要执行操作的字符串(比如view或pick)。
在与broadcast相关的intent里,就表示会发生并报告的操作。action大体上决定了intent剩余的部分的结构,具体来讲就是data和extra里包含什么。
虽然你可以自己定义action来启用自己应用中的组件(或为其他应用来调用你应用里的组件),但是你最好使用Intent类或其他框架类中已定义好的操作常量。下面是启动activity的一些常见操作:
你可以使用setAction()或Intent构造方法来为intent指定action。
如果你定义了自己的action,请确保它包含了你应用的包名作为前缀。例如:
Data一种URI(Uri对象),代表要操作的数据和/或数据的MIME类型。提供的data类型通常是intent的action决定的。例如,如果action是
在创建intent时,通常在data的URI里附加指定data的类型(MIME类型),这点非常重要。例如,可以显示图片的activity可能就不能播放音频文件了,尽管URI的格式看起来很相似。所以指定data的MIME类型可以帮助Android系统找到最适合的组件来接收你的intent。然而,MIME类型有时可以从URI中推出来,特别当data是
仅设置data的URI,请调用
注意:如果你想同时设置URI和MIME类型,不要调用
Category一种字符串,包含了一些附加的信息,关于应该捕获intent的组件的类别。虽然在intent里可以放置任意数量的category描述,但是大多数的intent甚至都不需要category。下面是一些常见category:
你可以使用
上面列举的这些属性(组件名称、action、data和category)可以定义独一无二的intent。通过读取这些属性,Android系统可以决定应该启动哪个组件。
然而,intent同样也可以携带其他信息,它们不会影响到决定启动哪个组件。intent也支持:
Extras键-值对携带了完成请求action必须的附加信息。就像某些action需要特定类型的data URI,有些action也需要特定的extra。
你可以使用多种putExtra()方法来添加extra数据,它们都需要两种参数:键的名称和值。你也可以创建一个Bundle对象来添加所有的extra数据,然后使用putExtras()方法把Bundle插入到Intent中。
例如,当使用ACTION_SEND创建了一个用来发送邮件的intent,你可以把“地址”指定给EXTRA_EMAIL键,把“主题”指定给EXTRA_SUBJECT键。
FlagsFlag定义在
更多信息,请参阅
例如,如果你在应用中构建了一个叫DownloadService的service,用来从网络上下载文件,那么你可以使用下面的代码来启动它:
// fileUrl是URL字符串,比如"http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(
startService(downloadIntent);[/code]
构造方法
更多关于构建和启动service的信息,请参阅Services指南。
隐式intent指定了action,它可以调用设备上任何可以执行该action的应用。当你的应用无法执行某个action,但是其他应用可能可以,并且你希望让用户来选择使用哪个应用,这种情形下使用隐式intent是非常有用的。
例如,如果你有一些想要用户分享给其他人的内容,使用ACTION_SEND action创建intent,然后把表示要分享内容的extra添加进来。当你使用那个intent调用
注意:也有可能用户的设备上没有任何可以处理你通过startActivity()发送过来的隐式intent的应用。如果真的那样,调用会失败并且你的应用会崩溃掉。为了验证是否有activity能接收到该intent,应该在你的Intent对象上调用resolveActivity()。如果结果是非null的,那么说明至少有一个应用可以处理该intent,这样就能安全的调用
// 验证该intent是否会匹配activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}[/code]
注意: 在这种情况下,虽然并没有使用到URI,但是通过声明intent的data类型来指定extra中携带的内容。
当调用了
图2. 选择对话框A chooser dialog.
当有不止一个应用可以响应你的隐式intent时,用户可以选择使用哪个应用并且使它作为该action的默认选择。对于执行那种用户以后可能愿意用相同应用处理的action来说,这是非常棒得功能。比如打开网页(用户更经常使用某种浏览器)。
然而,如果多种应用都可以响应该intent并且用户每次都想使用不同的应用来处理,那么你应该显式的显示选择对话框。该选择对话框每次都会询问用户使用哪个应用来处理该action(用户不能为该action选择默认的应用)。例如,当你的应用使用ACTION_SEND action来“分享”时,用户可能希望根据当前的情况来选择不同的应用来分享,所以你应该总是使用选择对话框,就像图2所示的。
使用createChooser()创建个Intent,然后把它传递给startActivity(),这样就能显示一个选择器了。例如:
然后就会显示一个对话框,内容是响应通过createChooser()方法传递过去的intent的应用列表,标题是传递的文本信息。
为了声明你的应用可以接收到哪些隐式intent,你应该在manifest file中使用
filter。每个intent filter都会根据intent的action、data和category来指定它能接收到的intent类型。如果intent能满足你的其中一个intent filter,系统就会把这个隐式intent分发给你的应用组件。
注意:不管该组件有没有声明其他的intent filter,该隐式intent总是会被分发给它的目标。
应用组件应该为每个它能处理的独立功能单独声明filter。例如,在图库应用中某个activity可能有两个filter:一个filter用来预览图片,另一个用来编辑图片。当启动activity时,它会检查Intent,然后根据该Intent中的信息决定操作(比如是否显示编辑选项)。
每个intent filter都是在应用的manifest file里通过
注意:为了接收隐式intent,你必须在intent filter中包含CATEGORY_DEFAULT category。方法
filter中这样声明,就不会有隐式intent传递给你的activity。
例如,下面是用intent filter声明的一个activity,它可以接收action为ACTION_SEND并且data是文本的intent:
创建一个包含多个
如果你想要处理多种不同的intent,但是它们仅仅匹配一些特定的action、data和category类型,那么你就需要创建多种intent filter了。
限制对组件的访问
使用intent filter来阻止其他应用启动你的应用不是一种安全的方式。尽管intent filter限制了组件只会响应某些特定的隐式intent,但是如果开发者公开了组件的名称,其他应用就可以悄悄的通过显式intent来启用你的组件。如果要求只有你自己应用才能启动你的组件,请把那些组件的
隐式intent会把intent里的这三种元素里的每样都拿来和filter进行测试对比。intent必须通过所有的这三种类型的测试后才能被分发给对应的组件。如果它一样都没匹配,那么Android系统不会把该intent分发给组件。不过,因为组件可能拥有多个intent filter,如果某个intent没有匹配组件的一个filter,它还有可能匹配另外一个filter。更多关于系统如何解析intent的信息请参阅下面关于Intent
Resolution的章节。
注意:请确保每次都使用显式intent去启动自己的service,并且不要为你的service声明intent filter,因为这样可以避免不经意间运行其他应用的
注意:对于activity来说,你必须在manifest文件声明intent filter。但是,board cast receiver的filter可以通过调用registerReceiver()来动态注册。然后你可以使用unregisterReceiver()来解除receiver。这样可以让你的应用只在运行期间的某个时间段里监听特定的broadcast。
为了更好的理解intent filter的行为,请阅读下面某个社交分享应用的manifest file的代码片段。
第一个activity,
data。this is the main entry point and does not expect any intent data.
为了在应用启动器中出现该activity,这两个必须成对出现。
第二个activity,
注意:这个MIME类型,
panorama API来处理它。
pending intent的主要使用情形包括:
声明当用户对你的Notification执行操作时,执行某个intent(Android系统会执行NotificationManager这个Intent)。
声明当用户对你的App Widget执行操作时,执行某个intent(桌面应用会执行这个Intent)。
声明会在未来的某个时间执行该intent(Android系统的AlarmManager会执行这个Intent)。
因为每个
intent时,你的应用不是调用诸如startActivity()等方法来执行该intent,而是通过调用相关构造方法创建PendingIntent时声明目标组件类型:
除非你的应用要接收其他应用的pending intent,否则你可能只用得到上面的PendingIntent方法来创建PendingIntent。
每种方法都需要当前应用的
更多关于使用pending intent的信息请查看对应使用案例的文档,例如Notifications和App
Widgets API指南。
当系统接收到一个启动activity的隐式intent时,它会基于下面三个方面来把intent与intent filter进行比较,找出最佳的activity:
intent action
intent data (URI和data类型都考虑)
intent category
下面的章节描述了intent如何依据应用的manifest file里的intent filter匹配合适的组件。
指定接收的intent action,intent filter可以声明0个或多个
要匹配这个filter的话,在Intent中指定的action必须要匹配上面列举的action之一。
如果filter中没有列举任何action,那么就没有东西给intent来匹配了,所有的intent都不会通过测试。但是,如果Intent没有指定任何action,那么它会通过测试(只要filter包含至少一个action)。
指定接收的intent category,intent filter可以声明0个或多个
intent要通过category测试的话,在Intent中的每个category都必须匹配filter中的category。反之不必要,intent
filter中可能声明的category比Intent还多,但是Intent依然会通过测试。因此,没有category的intent不管filter里声明了什么category都会通过测试。
注意:使用
filter中包含"android.intent.category.DEFAULT” category(如前面
指定接收的intent data,intent filter可以声明0个或多个
每个
例如:
在这个URI中,scheme是
在
如果没有指定schema,那么host就被忽略了。
如果没有指定host,那么port就被忽略了。
如果schema和host都没有被指定,那么path就被忽略了。
当把intent中的URI与filter中指定的URI标准进行比较时,只需要比较filter中包含的部分URI就行了。例如:
如果filter只指定了scheme,所有拥有那个scheme的URI都会匹配这个filter。
如果filter指定了scheme和authority,但是没有path,那么所有拥有相同scheme和authority的URI都会通过该filter,而不用管它们的path是什么。
如果filter指定了scheme、authority和path,那么只有拥有相同scheme、authority和path的才能通过该filter。
注意:path表达式可能包含星号(*)通配符,会匹配部分path名称。
data测试会把intent中的URI和MIME类型与filter中指定的URI和MIME类型进行比较。 规则如下:
既不包含URI,又不包含MIME类型的intent只能通过那些没有指定任何URI和MIME类型的filter的测试。
包含URI,但是不包含MIME类型(URI中没有隐含也无法推理出来)的intent只能通过那些URI格式匹配并且没有指定MIME类型的filter的测试。
包含MIME类型,但是不包含URI的intent只能通过那些有相同MIME类型并且没有指定URI格式的filer的测试。
既包含URI又包含MIME类型(URI隐含或者可以推理出来)的intent,它的MIME类型匹配filter中列举的MIME类型时,它才能通过MIME类型部分的测试,它的URI匹配filter中的URI,或它包含
最后一条规则,就是说组件可以从文件或content provider中获取本地数据。因此,它们的filter可以只列举data type,而不需要显式标明
provider中获取图片并显示:
因为大多数的数据都是通过content provider分发的,所以filter只指定data类型而不指定URI是很常见的。
另外一种常见得filter配置是使用scheme和data type。例如,像下面的
intent与intent filter比较不只是用来发现要启动的目标组件,同时也为发现设备上组件的其他信息。例如,Home应用通过寻找所有指定了action intent为
你的应用可以很容易的使用intent匹配。
receivers。
访问Android API指南目录索引查看全部翻译
Intent和Intent过滤器
Intent是一种消息对象,你可以使用它来请求其他应用组件(app
component)的操作。尽管intent有许多方式可以帮助组件之间进行交流,大体上有这三种最基础的用例:
启动activity:
在应用中,一个Activity代表一个页面。你可以传递一个Intent给startActivity()来启动一个新的Activity实例。该
Intent描述了要启动的activity,并且可以携带任何需要的数据。
调用startActivityForResult()来启动activity能在它结束时接收返回的结果。你的activity会在
onActivityResult()回调中接收到另外一个Intent对象作为返回的结果。更多信息,请参阅Activities指南。
启动service:
Service是一种可以不需要用户界面、在后台执行操作的组件。你可以传递一个Intent给startService()来开启一个service,通过service来执行一次性的操作(例如下载文件)。该
Intent描述了要启动的service,并且可以携带任何需要的数据。
如果service是使用客户端-服务器接口设计的,你可以从其他组件传递一个Intent给bindService()来绑定到service上。更多信息,请参阅Services指南。
分发broadcast:
broadcast是一种任何应用都能接收到的消息。系统会为系统事件分发不同的broadcast,比如系统启动时或设备充电时。你可以传递一个Intent给
sendBroadcast()、
sendOrderedBroadcast()或
sendStickyBroadcast()来把broadcast分发给其他应用。
Intent类型
intent有两种类型:显式intent 通过名称(全限定类名)来指定要启动的组件。你通常可以使用显式intent来启用你应用里的组件,因为你知道你想启动的activity和service的类名。例如,启动一个响应用户操作的新的activity或者启动一个在后台下载文件的service。
隐式intent 不用指定组件的名称,但是必须声明要执行的操作,这样其他应用的组件就能捕获到它。例如,如果你想在地图上显示用户的位置,你可以使用隐式intent去请求其他满足条件的应用在地图上显示具体的位置。
当你创建了显式intent来启动activity或service时,系统就会立即启动在Intent对象中指定的组件。
图1. 插图显示了隐式intent如何通过系统分发来启动另外的activity:[1] Activity A创建带操作描述的
Intent,然后把它传递给
startActivity()。 [2] Android系统搜索所有的应用来寻找匹配该intent的intent
filter。当有匹配时,[3] 系统通过把Intent传递给匹配的activity(Activity
B)然后调用它的
onCreate()方法启动它。
当你创建了隐式intent时,Android系统就会通过把intent里的内容与设备上其他应用在manifest
file中声明的intent filter进行比较来找出合适的组件启动。如果intent能匹配到一个intent
filter,系统就会启动那个组件,并把Intent对象传递给它。如果匹配了不止一个intent
filter,系统就会显示一个对话框,让用户来挑选使用哪个应用。
intent filter是一种在应用manifest文件里的表达式,它说明了组件想要收到的intent类型。例如,为activity声明intent filter,其他应用就能使用某种确定类型的intent启动你的activity。同样地,如果你没有为activity声明任何intent filter,那么它只能被显式intent启动。
注意:为了保证应用的安全性,在启动Service时请确保使用显式intent,并且不要为你的service声明intent filter。使用隐式intent启动service是有安全风险的,因为你不能确定什么service会响应你的intent,并且用户也不知道哪个service启动了。
构建Intent
Intent对象携带了一些信息,Android系统可以使用它们来决定启动哪个组件(例如确切的组件名或可以接收该intent的组件类别),为了确保接收的组件能正确的执行操作,intent还可以携带一些附加的信息给接收的组件使用(例如要执行的操作和处理的数据)。
Intent中主要包含的信息如下:
组件名称
要启动组件的名称。
这是可选的,但是建议使用显示intent,这意味着该intent只会被分发给组件名称定义的应用组件。如果没有组件名称,那么这个intent就是隐式的,系统会基于intent中的其他信息决定应该把该intent分发给哪个组件(例如下面描述的操作、数据和类别)。所以如果你希望启动应用中某个特定的组件,那么你应该指定组件的名称。
注意:启动
Service时,你应该永远指定组件的名称。 否则,你无法确定哪个service会响应那个intent,并且用户也不知道哪个service启动了。
Intent里名称的域变量是
ComponentName对象,你可以使用目标组件的包含应用包名的全限定类名来指定。例如,
com.example.ExampleActivity。你可以使用
setComponent()、
setClass()、
setClassName()或Intent的构造方法来设置组件名称。
Action表示要执行操作的字符串(比如view或pick)。
在与broadcast相关的intent里,就表示会发生并报告的操作。action大体上决定了intent剩余的部分的结构,具体来讲就是data和extra里包含什么。
虽然你可以自己定义action来启用自己应用中的组件(或为其他应用来调用你应用里的组件),但是你最好使用Intent类或其他框架类中已定义好的操作常量。下面是启动activity的一些常见操作:
ACTION_VIEW当你有些信息需要启动activity来展示给用户看,可以在startActivity()处理的intent里使用这个action,比如在相册应用里预览图片,或在地图应用中查看地址。
ACTION_SEND也被称为“分享”intent,当你有些数据需要通过其他应用来分享时,可以在startActivity()处理的intent里使用这个action,例如邮件应用或社交分享应用。请参阅
Intent类的参考文档获取更多定义action的常量。其他action定义在Android框架的其他地方,例如在Settings中定义了一些打开系统设置应用指定页面的action。
你可以使用setAction()或Intent构造方法来为intent指定action。
如果你定义了自己的action,请确保它包含了你应用的包名作为前缀。例如:
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
Data一种URI(Uri对象),代表要操作的数据和/或数据的MIME类型。提供的data类型通常是intent的action决定的。例如,如果action是
ACTION_EDIT,那么data应该包含要编辑文档的URI。
在创建intent时,通常在data的URI里附加指定data的类型(MIME类型),这点非常重要。例如,可以显示图片的activity可能就不能播放音频文件了,尽管URI的格式看起来很相似。所以指定data的MIME类型可以帮助Android系统找到最适合的组件来接收你的intent。然而,MIME类型有时可以从URI中推出来,特别当data是
content(一种指明数据存储在设备位置的URI,受到可以使data得MIME类型对系统可见的ContentProvider控制)时。
仅设置data的URI,请调用
setData()。仅设置MIME类型,请调用
setType()。如果需要的话,你可以使用setDataAndType()同时设置它们。
注意:如果你想同时设置URI和MIME类型,不要调用
setData()和
setType(),因为它们都会让对方的值失效。一定要使用
setDataAndType()来设置URI和MIME类型。
Category一种字符串,包含了一些附加的信息,关于应该捕获intent的组件的类别。虽然在intent里可以放置任意数量的category描述,但是大多数的intent甚至都不需要category。下面是一些常见category:
CATEGORY_BROWSABLE目标activity允许自己被浏览器启动来显示链接引用的数据,例如图像或邮件信息。
CATEGORY_LAUNCHERactivity是任务栈里最顶端的activity,并在系统应用启动器里列举出来。请参阅
Intent类的描述查看全部的category列表。
你可以使用
addCategory()来指定category。
上面列举的这些属性(组件名称、action、data和category)可以定义独一无二的intent。通过读取这些属性,Android系统可以决定应该启动哪个组件。
然而,intent同样也可以携带其他信息,它们不会影响到决定启动哪个组件。intent也支持:
Extras键-值对携带了完成请求action必须的附加信息。就像某些action需要特定类型的data URI,有些action也需要特定的extra。
你可以使用多种putExtra()方法来添加extra数据,它们都需要两种参数:键的名称和值。你也可以创建一个Bundle对象来添加所有的extra数据,然后使用putExtras()方法把Bundle插入到Intent中。
例如,当使用ACTION_SEND创建了一个用来发送邮件的intent,你可以把“地址”指定给EXTRA_EMAIL键,把“主题”指定给EXTRA_SUBJECT键。
Intent类为标准数据类型指定了许多
EXTRA_*常量。如果你需要声明自己的extra键(为你的应用接收的intent使用),请确保包含你应用的包名作为前缀。例如:
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
FlagsFlag定义在
Intent类中,对于intent来说它的功能就像元数据。flag可能会告诉Android系统如何启动activity(例如,activity应该属于哪个task,启动后如何处理activity(例如,是否在最近启动activity列表中))。
更多信息,请参阅
setFlags()方法。
显式intent事例
你可以使用显式intent启动指定的应用组件,例如你应用中某个特定的activity或service。为Intent对象指定组件名称就能创建显式的intent了,其他的intent属性是可选的。例如,如果你在应用中构建了一个叫DownloadService的service,用来从网络上下载文件,那么你可以使用下面的代码来启动它:
// 在Activity中执行,所以'this'即[code]Context
// fileUrl是URL字符串,比如"http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(
Uri.parse(fileUrl));
startService(downloadIntent);[/code]
构造方法
Intent(Context, Class)传递应用的
Context和组件的
Class对象。像这样,intent显式的启动了应用中的
DownloadService类。
更多关于构建和启动service的信息,请参阅Services指南。
隐式intent事例
隐式intent指定了action,它可以调用设备上任何可以执行该action的应用。当你的应用无法执行某个action,但是其他应用可能可以,并且你希望让用户来选择使用哪个应用,这种情形下使用隐式intent是非常有用的。例如,如果你有一些想要用户分享给其他人的内容,使用ACTION_SEND action创建intent,然后把表示要分享内容的extra添加进来。当你使用那个intent调用
startActivity()后,用户就能选择使用哪个应用来分享内容了。
注意:也有可能用户的设备上没有任何可以处理你通过startActivity()发送过来的隐式intent的应用。如果真的那样,调用会失败并且你的应用会崩溃掉。为了验证是否有activity能接收到该intent,应该在你的Intent对象上调用resolveActivity()。如果结果是非null的,那么说明至少有一个应用可以处理该intent,这样就能安全的调用
startActivity()了。如果结果是null的,那么你不应该使用这个intent,如果可能的话,你应该禁用会发出该intent的功能。
// 创建字符串文本消息 Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); sendIntent.setType([code]HTTP.PLAIN_TEXT_TYPE); // MIME类型为"text/plain"
// 验证该intent是否会匹配activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}[/code]
注意: 在这种情况下,虽然并没有使用到URI,但是通过声明intent的data类型来指定extra中携带的内容。
当调用了
startActivity(),系统会检查所有安装的应用来确定哪个应用应该处理这种类型的intent(action类型为ACTION_SEND,并且携带"text/plain”数据的intent)。如果只有一个应用可以处理它,那么会立即打开那个应用,并且该intent会传递给那个应用。 如果多个activity都可以处理该intent,那么系统会给用户显示一个对话框,让用户来选择使用哪个应用。
图2. 选择对话框A chooser dialog.
强制应用选择器
当有不止一个应用可以响应你的隐式intent时,用户可以选择使用哪个应用并且使它作为该action的默认选择。对于执行那种用户以后可能愿意用相同应用处理的action来说,这是非常棒得功能。比如打开网页(用户更经常使用某种浏览器)。然而,如果多种应用都可以响应该intent并且用户每次都想使用不同的应用来处理,那么你应该显式的显示选择对话框。该选择对话框每次都会询问用户使用哪个应用来处理该action(用户不能为该action选择默认的应用)。例如,当你的应用使用ACTION_SEND action来“分享”时,用户可能希望根据当前的情况来选择不同的应用来分享,所以你应该总是使用选择对话框,就像图2所示的。
使用createChooser()创建个Intent,然后把它传递给startActivity(),这样就能显示一个选择器了。例如:
Intent intent = new Intent(Intent.ACTION_SEND); ... // 对于UI上的文本请使用字符串资源。 // 这里说些什么,比如“Share this photo with” String title = getResources().getString(R.string.chooser_title); // 创建intent来显示选择器 Intent chooser = Intent.createChooser(intent, title); // 检验至少有一个activity可以匹配该intent if (sendIntent.resolveActivity(getPackageManager()) != null) { startActivity(sendIntent); }
然后就会显示一个对话框,内容是响应通过createChooser()方法传递过去的intent的应用列表,标题是传递的文本信息。
接收隐式Intent
为了声明你的应用可以接收到哪些隐式intent,你应该在manifest file中使用<intent-filter>元素为每个应要组件声明一个或多个intent
filter。每个intent filter都会根据intent的action、data和category来指定它能接收到的intent类型。如果intent能满足你的其中一个intent filter,系统就会把这个隐式intent分发给你的应用组件。
注意:不管该组件有没有声明其他的intent filter,该隐式intent总是会被分发给它的目标。
应用组件应该为每个它能处理的独立功能单独声明filter。例如,在图库应用中某个activity可能有两个filter:一个filter用来预览图片,另一个用来编辑图片。当启动activity时,它会检查Intent,然后根据该Intent中的信息决定操作(比如是否显示编辑选项)。
每个intent filter都是在应用的manifest file里通过
<intent-filter>元素定义的,并且嵌套在相关的应用组件中(比如
<activity>元素)。在
<intent-filter>中,你可以使用下面三个元素中的一个或多个来指定它能接受的intent类型:
<action>在
name属性里声明intent接收的action。值必须是action的字面字符串值,而不是类常量。
<data>使用一个或多个能指定data URI各方面信息(
scheme、
host、
port、
path等)和MIME类型的属性来声明接收的data类型。
<category>在
name属性里声明intent接收的category 。值必须是action的字面字符串值,而不是类常量。
注意:为了接收隐式intent,你必须在intent filter中包含CATEGORY_DEFAULT category。方法
startActivity()和
startActivityForResult()默认把所有的intent当做声明为CATEGORY_DEFAULT category来处理。如果你没有在intent
filter中这样声明,就不会有隐式intent传递给你的activity。
例如,下面是用intent filter声明的一个activity,它可以接收action为ACTION_SEND并且data是文本的intent:
<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>
创建一个包含多个
<action>、
<data>、或
<category>的filter是允许的。如果你这么做了,只需要确保那个组件能匹配这个filter元素中的所有元素。
如果你想要处理多种不同的intent,但是它们仅仅匹配一些特定的action、data和category类型,那么你就需要创建多种intent filter了。
限制对组件的访问
使用intent filter来阻止其他应用启动你的应用不是一种安全的方式。尽管intent filter限制了组件只会响应某些特定的隐式intent,但是如果开发者公开了组件的名称,其他应用就可以悄悄的通过显式intent来启用你的组件。如果要求只有你自己应用才能启动你的组件,请把那些组件的
exported属性设置为
“false”。
隐式intent会把intent里的这三种元素里的每样都拿来和filter进行测试对比。intent必须通过所有的这三种类型的测试后才能被分发给对应的组件。如果它一样都没匹配,那么Android系统不会把该intent分发给组件。不过,因为组件可能拥有多个intent filter,如果某个intent没有匹配组件的一个filter,它还有可能匹配另外一个filter。更多关于系统如何解析intent的信息请参阅下面关于Intent
Resolution的章节。
注意:请确保每次都使用显式intent去启动自己的service,并且不要为你的service声明intent filter,因为这样可以避免不经意间运行其他应用的
Service。
注意:对于activity来说,你必须在manifest文件声明intent filter。但是,board cast receiver的filter可以通过调用registerReceiver()来动态注册。然后你可以使用unregisterReceiver()来解除receiver。这样可以让你的应用只在运行期间的某个时间段里监听特定的broadcast。
filter事例
为了更好的理解intent filter的行为,请阅读下面某个社交分享应用的manifest file的代码片段。<activity android:name="MainActivity"> <!-- 这个activity是主入口,应该显示在应用启动器上 --> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="ShareActivity"> <!-- 这个activity处理带文本数据的“SEND” action --> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> <!-- 这个activity可以处理媒体数据的“SEND”和“SEND_MULTIPLE” --> <intent-filter> <action android:name="android.intent.action.SEND"/> <action android:name="android.intent.action.SEND_MULTIPLE"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="application/vnd.google.panorama360+jpg"/> <data android:mimeType="image/*"/> <data android:mimeType="video/*"/> </intent-filter> </activity>
第一个activity,
MainActivity,是应用的主入口,用户通过应用的启动图标启动应用后就打开这个activity了:
ACTION_MAINaction说明这是主入口并且不需要任何intent
data。this is the main entry point and does not expect any intent data.
CATEGORY_LAUNCHERcategory说明这个activity的图标应该出现在系统的应用启动器中。如果
<activity>元素没有使用icon指定图标,那么系统会使用
<application>元素中的图标。
为了在应用启动器中出现该activity,这两个必须成对出现。
第二个activity,
ShareActivity,被用来分享文本和媒体内容。尽管用户可能直接从MainActivity的导航功能进入该activity,它们同样也可以直接从其他应用发出匹配这两个intent filter之一的的隐式intent来进入
ShareActivity。
注意:这个MIME类型,
application/vnd.google.panorama360+jpg,是一种指定全景图片的特殊数据类型,你可以使用Google
panorama API来处理它。
使用Pending Intent
PendingIntent对象是对Intent对象的包装。
PendingIntent的主要作用是给外部应用权限来使用被包含的intent,就好像在你的应用自己的进程中执行一样。
pending intent的主要使用情形包括:
声明当用户对你的Notification执行操作时,执行某个intent(Android系统会执行NotificationManager这个Intent)。
声明当用户对你的App Widget执行操作时,执行某个intent(桌面应用会执行这个Intent)。
声明会在未来的某个时间执行该intent(Android系统的AlarmManager会执行这个Intent)。
因为每个
Intent对象都被设计成被特定类型的应用组件处理(
Activity,
Service或
BroadcastReceiver),所以
PendingIntent和他们情况相同。在使用pending
intent时,你的应用不是调用诸如startActivity()等方法来执行该intent,而是通过调用相关构造方法创建PendingIntent时声明目标组件类型:
PendingIntent.getActivity()针对启动Activity的
Intent。
PendingIntent.getService()针对启动Service的
Intent。
PendingIntent.getBroadcast()针对启动BroadcastReceiver的
Intent。
除非你的应用要接收其他应用的pending intent,否则你可能只用得到上面的PendingIntent方法来创建PendingIntent。
每种方法都需要当前应用的
Context,和你想要包装的
Intent,还有一个或多个用来指定该intent应该如何被使用的flag(比如该intent是否能被多次使用)。
更多关于使用pending intent的信息请查看对应使用案例的文档,例如Notifications和App
Widgets API指南。
Intent解析
当系统接收到一个启动activity的隐式intent时,它会基于下面三个方面来把intent与intent filter进行比较,找出最佳的activity:intent action
intent data (URI和data类型都考虑)
intent category
下面的章节描述了intent如何依据应用的manifest file里的intent filter匹配合适的组件。
Action测试
指定接收的intent action,intent filter可以声明0个或多个<action>元素。例如:
<intent-filter> <action android:name="android.intent.action.EDIT" /> <action android:name="android.intent.action.VIEW" /> ... </intent-filter>
要匹配这个filter的话,在Intent中指定的action必须要匹配上面列举的action之一。
如果filter中没有列举任何action,那么就没有东西给intent来匹配了,所有的intent都不会通过测试。但是,如果Intent没有指定任何action,那么它会通过测试(只要filter包含至少一个action)。
Category测试
指定接收的intent category,intent filter可以声明0个或多个<category>元素。例如:
<intent-filter> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> ... </intent-filter>
intent要通过category测试的话,在Intent中的每个category都必须匹配filter中的category。反之不必要,intent
filter中可能声明的category比Intent还多,但是Intent依然会通过测试。因此,没有category的intent不管filter里声明了什么category都会通过测试。
注意:使用
startActivity()和
startActivityForResult()时,Android会自动的在所有的隐式intent中添加CATEGORY_DEFAULT category。所以如果你想要你的activity能接收到这些隐式intent的话,就必须在它的intent
filter中包含"android.intent.category.DEFAULT” category(如前面
<intent-filter>例子所示)。
Data测试
指定接收的intent data,intent filter可以声明0个或多个<data>元素。例如:
<intent-filter> <data android:mimeType="video/mpeg" android:scheme="http" ... /> <data android:mimeType="audio/mpeg" android:scheme="http" ... /> ... </intent-filter>
每个
<data>元素可以指定一个URI结构和一个数据类型(MIME媒体类型)。有许多独立的属性,
scheme、
host、
port
和
path,对应着URI的各个部分:
<scheme>://<host>:<port>/<path>
例如:
content://com.example.project:200/folder/subfolder/etc
在这个URI中,scheme是
content,host是
com.example.project,端口是
200,path是
folder/subfolder/etc。
在
<data>元素中的这些属性都是可选的,但是它们有线形依赖关系:
如果没有指定schema,那么host就被忽略了。
如果没有指定host,那么port就被忽略了。
如果schema和host都没有被指定,那么path就被忽略了。
当把intent中的URI与filter中指定的URI标准进行比较时,只需要比较filter中包含的部分URI就行了。例如:
如果filter只指定了scheme,所有拥有那个scheme的URI都会匹配这个filter。
如果filter指定了scheme和authority,但是没有path,那么所有拥有相同scheme和authority的URI都会通过该filter,而不用管它们的path是什么。
如果filter指定了scheme、authority和path,那么只有拥有相同scheme、authority和path的才能通过该filter。
注意:path表达式可能包含星号(*)通配符,会匹配部分path名称。
data测试会把intent中的URI和MIME类型与filter中指定的URI和MIME类型进行比较。 规则如下:
既不包含URI,又不包含MIME类型的intent只能通过那些没有指定任何URI和MIME类型的filter的测试。
包含URI,但是不包含MIME类型(URI中没有隐含也无法推理出来)的intent只能通过那些URI格式匹配并且没有指定MIME类型的filter的测试。
包含MIME类型,但是不包含URI的intent只能通过那些有相同MIME类型并且没有指定URI格式的filer的测试。
既包含URI又包含MIME类型(URI隐含或者可以推理出来)的intent,它的MIME类型匹配filter中列举的MIME类型时,它才能通过MIME类型部分的测试,它的URI匹配filter中的URI,或它包含
content:或
file:URI并且filter没有指定URI时,它才能通过URI部分的测试。
最后一条规则,就是说组件可以从文件或content provider中获取本地数据。因此,它们的filter可以只列举data type,而不需要显式标明
content:和
file:scheme。这是一种典型得使用方式。例如,像下面的
<data>元素,告诉Android这个组件可以从content
provider中获取图片并显示:
<intent-filter> <data android:mimeType="image/*" /> ... </intent-filter>
因为大多数的数据都是通过content provider分发的,所以filter只指定data类型而不指定URI是很常见的。
另外一种常见得filter配置是使用scheme和data type。例如,像下面的
<data>元素,告诉Android这个组件可以从网络上获取视频数据来播放:
<intent-filter> <data android:scheme="http" android:type="video/*" /> ... </intent-filter>
Intent匹配
intent与intent filter比较不只是用来发现要启动的目标组件,同时也为发现设备上组件的其他信息。例如,Home应用通过寻找所有指定了action intent为ACTION_MAINaction和
CATEGORY_LAUNCHERcategory的activity来放置应用启动器。
你的应用可以很容易的使用intent匹配。
PackageManager有一系列得
query...()方法,它可以返回所有可以接收某个特定intent的组件,同样,还有一系列的
resolve...()方法,可以找出最适合响应某个intent的组件。例如,
queryIntentActivities()会返回能执行参数intent的所有activity列表,同样,
queryIntentServices()会返回的service列表。所有的方法都不会激活组件的,它们只是列举它们能影响的组件列表。同样,
queryBroadcastReceivers()针对broadcast
receivers。
相关文章推荐
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- 解決Linux下Android开发真机调试设备不被识别问题
- [Android]在代码里运行另一个程序的方法
- [软件咨询]WPS2012正式版已发布 金山Office移动版4.0发布
- Android笔记-Linux Kernel Ftrace (Function Trace)解析
- 一个小型js框架myJSFrame附API使用帮助
- 详细分析交换机、路由器、集线器的区别和联系
- 批处理的api WMIC学习体会有感第1/2页
- 批处理 API实现文件下载的代码第1/2页
- 强制删除工具 xdelbox xdelbox1.5正式版下载
- 揪出交换机端口背后“凶手”导致网速太慢
- 电脑重启后突然检测不到硬盘的原因分析与解决办法
- PQ分区出错! 巧用Ghost急速补救的绝妙办法
- android USB如何修改VID具体实现
- Android增量升级的方法和原理详细介绍
- Android Mouse实现过程详细笔记
- 深入Android Browser配置管理的详解