Intents and Intent Filters

Intents and Intent Filters

翻译目的:
App协作泄露隐私,肯定需要研究多个App间组件是否能够匹配,因此,翻译了Intents and Intent Filters。
基础概念
Intent是一个消息对象,你可以用它从another app component请求活动(action)。尽管intents可以有多种方式促进组件间的通信,但是均是以下述为基础的。

  1. 启动Activity:

    如果你想得到被启动activity关闭时返回的结果,可以使用startActivityForResult()启动新的activity,在onActivityResult(int resquestCode,int resultCode,Intent intent)中接收结果。

  2. 启动Service:
    Service是一个运行在后台没有界面的执行操作的组件。可以通过将一个Intent传递给startService()来执行one-time operation(例如下载一个文件)。Intent描述了启动的service和携带的必须的数据。
    bindService…
  3. 分发广播:
    broadcast是任何app都可以接受的message。系统会分发很多的广播,例如:当系统启动或设备充电。你可以通过将Intent传递给sendBroadcast()、sendOrderdBroadcast()、sendStickyBroadcast()来分发一个广播给其他app。

隐式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/mpegandroid:scheme=”http” …/>
    <data android:mimeType=”audio/mpegandroid: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]

你可能感兴趣的:(android,intent,Inent隐式匹配)