翻译目的:
App协作泄露隐私,肯定需要研究多个App间组件是否能够匹配,因此,翻译了Intents and Intent Filters。
基础概念:
Intent是一个消息对象,你可以用它从another app component请求活动(action)。尽管intents可以有多种方式促进组件间的通信,但是均是以下述为基础的。
启动Activity:
如果你想得到被启动activity关闭时返回的结果,可以使用startActivityForResult()启动新的activity,在onActivityResult(int resquestCode,int resultCode,Intent intent)中接收结果。
隐式Intent:
Implicit intents没有以一个指定的component命名,而是声明需要执行的操作,该操作允许第三方app的component去处理它。例如:如果你想在地图上展示用户的位置,你可以使用一个implicit intent去请求第三方app在地图上展示指定的位置。
当你创建一个implicit intent,Android系统通过比较intent的内容与设备中other app的manifest file文件中声明的intent filters,来寻找适合的组件去启动。如果该intent匹配一个intent filter,系统启动该组件并传递Intent对象。如果多个intent filter匹配,系统将展示一个对话框,让用户选择一个app去执行。
intent filter是一个在app manifest文件中指定该组件能够接收的intent类型的表达式。例如:通过给一个activity声明一个intent filter,其他app将可以直接通过某一种intent来启动你的activity。然而,如果你没有声明intent filter,那么只能通过显示intent启动。
创建一个Intent:
Intent对象携带一些信息,Android系统将使用这些信息去判断哪个组件将被启动。
Intent中包含的主要信息如下:
Component name:
Action:
一个指定需要执行操作的string。
这个action很大程度上决定了intent剩下参数的结构。你可以自定义action,也可以使用Intent类中的action常量,如ACTION_VIEW、ACTION_SEND等。
Data:
被操作数据的引用的URL和数据的MIME type。数据的类型一般都是由intent的action来指定的。例如:action是ACTION_EDIT时,data应该包含document的URL去edit。
当创建一个intent时,声明数据类型和数据的URL通常是重要的。例如:一个展示图片的activity不能播放音频文件,尽管URL格式可能是相同的。因此,声明数据的MIME类型将帮助Android系统寻找到最好的组件使其接收你的intent。然而,MIME type有时候从URL中被推测-特别当数据是content:URL,这表示数据位于设备中,并且被ContentProvider控制,这将使MIME type针对系统可见。
仅设置数据URL,使用setData();仅设置MIME type,使用setType();如果需要可以使用setDataAndType()两个都设置(不能使用setData()和setType()分别设置URL和MIME type)。
Category:
指定需要处理intent的组件必须包含的额外信息的string。一般指示组件被启动的环境。一个intent可以声明任意数量的category,但是most intents不需要category。
可以使用addCategory()增加一般category,如CATEGORY_BROWSABLE、CATEGORY_LAUNCHER等外,还可以增加自定义的category。
上述属性(component name,action,data,category)定义了一个intent的特征。通过阅读这些属性,Android系统能够决定启动哪个app组件。
然而,一个intent可以携带额外的信息,这些信息不会影响Android系统选择哪个app组件。
Extras:
Key-value携带所需的额外信息来完成请求的操作。例如:一些action使用特定类型的data URLs,一些action也需要particular extras。
可以使用多种类型的putExtra()来增加extra data,接收两个参数:the key name和the value;或创建Bundle对象,增加所有的extra data,再将Bundle作为参数赋给putExtras()。
Intent同样提供很多EXTRA_*的常量Key,用户也可以自定义Key
Flags:
Example implicit intent:
隐式的intent指定一个action,可以调用设备中任何可以执行该action的app。当你的app不能执行这个action时,使用一个隐式的intent是非常有用的。其他的多个app可能满足且你可以选择一个app去使用。
当然可能设备中任何app都不能执行该action,那么调用将会失败,你的app也会崩溃。为了验证存在一个app可以接收该intent,可以调用new Intent().resolveActivity(getPackageManager())来判断,如果结果为空,则不应该使用intent。
一般存在多个app满足intent,用户选择一个作为默认处理的app。如果让该action每次都根据不同的situation,由用户选择,那么就应该使用createChooser(),并将该Intent传递给startActivity
例如:
Intent sendIntent = new Intent(Intent.ACTION_SEND);
…
String title = …
Intent chooser = Intent.createChooser(sendIntent,title);
If(sendIntent.resolveActivity(getPackageManager())!=null)
startActivity(chooser);
Receiving an Implicit Intent:
为了说明你的app可以接收哪些implicit intents,为你的app的每个组件使用<intent-filter>
声明一个或多个intent filters。每个intent filter指定app可以接收的intent类型(根据intent的action、data、category)。当且仅当intent满足你的intent filters中的一个(可以为一个组件设置多个<intent-filter>
),系统将传递一个implicit intent到你的app组件。
<action>
在name属性中声明可以接收的intent action.The value must be the literal string value of an action,not the class constant.
<data>
声明可以接收的数据类型,using one or more attributes that specify various aspects of the data URL(scheme,host,port,path,etc.)and MIME type.
<category>
声明可以接收的intent category,The value must be the literal string value of an action,not the class constant.
注释:为了能够接收隐式的intent,<intent-filter>
中必须包含CATEGORY_DEFAULT category。方法startActivity()和startActivityForResult()认为所有的intents默认声明了CATEGORY_DEFAULT category。如果<intent-filter>
中没有声明该category,那么没有隐式的intent将传递给该activity。
注:为了避免无心的启动一个不同app中的service,通常使用一个explicit intent去启动自己的service,并且不给你的service声明intent filter.
对于所有的activity,你只能在manifest file中声明你的intent filter。然而,针对broadcast receivers的filter,可以通过调用registerReceiver()动态的注册。然后,使用unregisterReceiver()取消注册。
Restricting access to components:
使用intent filter不是一个安全的方法去阻止其他apps启动你的组件。尽管intent filter约束一个组件仅去响应某一类的隐式intent,但是另外一些app可以通过显式的intent启动你的component。如果你的component是非常重要的,仅希望自己的component调用它,那么应该设置exported的值为false.
<activity android:name=”MainActivity”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
ACTION_MAIN action指示这是一个main entry并且不希望任何的intent data.
CATEGORY_LAUNCHER category指示这个activity的icon应该被放在system app的launcher中.
Intent Resolution:
当系统接收一个隐式intent去启动一个activity时,它会通过比较intent与intent filters的值(action、data<URL和type>
、category),寻找最优的activity。
如何匹配:
Action test:
指定可以接收的intent actions,一个intent filter可以声明零个或多个<action>
元素:
<intent-filter>
<action android:name=”android.intent.action.EDIT” />
<action android:name=”android.intent.action.VIEW” />
…
</intent-filter>
为了能通过这个filter,在Intent中指定的action必须匹配filter中actions中的一个。
如果filter没有列出任何actions,将没有任何信息让Intent去匹配,因此所有的intents匹配失败。然而,如果一个Intent没有指定一个action,它将通过匹配(filter包含至少一个action)。
Category test:
指定可以接收的intent categories,一个intent filter可以声明零个或多个<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或许声明的categories比intent中指定的多,Intent仍然可以匹配成功。因此,一个没有携带categories的intent可以通过匹配,忽略filter中声明的categories。
注:Android自动化的给所有implicit intents添加CATEGORY_DEFAULT category,传递给startActivity()和startActivityForResult().如果你希望自己的activity能够接收隐式intents,那么在intent filters中必须包含category “android.intent.category.DEFAULT”.
Data test:
指定可接收的intent data,一个intent filter可以申请0个或多个<data>
元素。
<intent-filter>
<data android:mimaType=”video/mpeg” android:scheme=”http” …/>
<data android:mimeType=”audio/mpeg” android:scheme=”http” …/>
…
</intent-filter>
每个<data>
元素指定了一个URI structure和data type(MIME media type).每个URL可以划分为这些属性-scheme,host,port,path.
<scheme>://<host>:<port>/<path>
例如:
content://com.example.project:200/folder/subfolder/etc
在这个URI中,scheme是content,host是com.example.project,port是200,path是/folder/subfolder/etc
在<data>
元素中这些属性是可选的,但是它们是线性依赖的:
如果scheme没有指定,则host被忽略;
如果host没有指定,则port被忽略;
如果scheme和host都没有指定,则path被忽略。
当intent中的URI和在filter中指定的URI比较时,仅当URI的部分被filter包含,例如:
如果filter仅仅指定了scheme,所有携带该scheme的URIs都能匹配该filter;
如果filter指定了scheme和authority,但是没有path,所有携带相同scheme和authority的URIs都能匹配成功,忽略intent中URI的path;
如果filter指定了scheme,authority和path,仅仅携带相同的scheme,authority,path的URIs才能匹配成功。
当URI和MIME type都存在时的匹配规则如下:
一个intent既没有URI也没有MIME type,仅当filter没有指定任何URIs和MIME type时才能匹配;
一个intent包含URI没有MIME type(没有显式也不能从URI中推理得到),仅当intent的URI匹配filter的URI格式,并且filter没有指定MIME type;
一个intent包括MIME type但是没有URI,仅当filter列表中具有相同的MIME type并且没有声明URI格式;
一个intent既包括URI也包含MIME type(显式声明或能从URI中推测得到),如果Intent的类型匹配filter中的类型,则通过了MIME type part。如果Intent的URI匹配filter的URI或者Intent有一个content:、file:的URI,并且filter仅仅列出了MIME type,则Intent通过URI部分的匹配。In other words, a component is presumed to support content: and file: data if its filter lists only a MIME type。
最后一个规则反应:组件能够从file或者content provider中得到本地数据。因此,它们的filter可以仅列出data type且不需要显示的命名content:和file: schemes。这是一个典型的例子:<data>
元素告诉Android,组件可以从content provider中得到image并展示它:
<intent-filter>
<data android:mimeType=”image/*” />
…
</intent-filter>
因为很多avaiable data被content providers分发,filters指定数据类型二没有URI是非常普遍的;
另外一个普通配置是使用scheme和data type的filter。例如:<data>
元素告诉Android,组件可以接收网上的video数据以达到执行action的目的:
<intent-filter>
<data android:scheme=”http” android:type=”video/*” />
…
</intent-filter>
总结:
Intent中只有action时,匹配有可能成功;
Intent中只有category,没有action匹配不可能成功;
Intent中只有data,没有action匹配不可能成功;
Intent中只有data和MIME type,没有action匹配不可能成功;
Intent-filter中必须包含,否则将无法被隐式启动;
Intent-filter中任何一个需要隐式启动的Activity都必须包含<category android:name=”android.intent.category.DEFAULT” />
;
Intent-filer中activity组件可以声明多对<intent-filter></intent-filter>
,只要匹配一对,即可启动这个Activity;
Intent-filter里可以有多个<action android:name=”” />
,只需匹配其中一个即可;
上述这些全部适用于Service和BroadcastReceiver。
注:能够被外部app调用的一个重要的元素是exported=”true”;
exported元素的默认值:存在<intent-filter>
时,默认为true,否则为false;
而broadcastreceiver则不是,只有sdk<16时,默认为true,否则为false;
exported只能限制外部app调用,不能限制app内部组件间调用。
[http://developer.android.com/guide/components/intents-filters.html]