Android中隐式Intent的用途(转)

转自:http://blog.csdn.net/coder80/article/details/7879259 和  http://blog.csdn.net/coder80/article/details/7881406

感谢原作者。

========================================================================================

 对于明确指出了目标组件名称的Intent,我们称之为“显式Intent”对于没有明确指出目标组件名称的Intent,则称之为“隐式Intent”。Android系统使用Intent Filter 来寻找与隐式Intent相关的对象.

        隐式Intent往往用于不同应用程序之间的使用。由于开发人员往往并不清楚别的应用程序的组件名称,因此,隐式Intent便尤为重要

对于隐式Intent,由于没有明确的目标组件名称,所以必须由Android系统帮助应用程序寻找与Intent请求意图最匹配的组件具体的选择方法是:Android将Intent的请求内容和一个叫做intent-filter比较,Intent Filter中包含系统中所有可能的待选组件如果Intent Filter中某一组件匹配隐式Intent请求的内容,那么Android就选择该组件作为该隐式Intent的目标组件.

       Android 如何知道应用程序能够处理某种类型的Intent 请求呢?这需要应用程序在AndroidManifest.xml中声明自己所含组件的过滤器(即可以匹配哪些Intent请求)一个没有声明Intent Filter的组件只能响应指明自己名字的显式Intent请求,而无法响应隐式Intent请求在配置文件中,常见的intent-filter声明如下:

<intent-filter android:label="@string/resolve_edit">

    <action android:name="android.intent.action.VIEW" />

    <action android:name="android.intent.action.EDIT" />

    <action android:name="com.android.notes.action.EDIT_NOTE" />

    <category android:name="android.intent.category.DEFAULT"/>

    <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />

</intent-filter>

而一个声明了intent-filter的组件既可以响应显式Intent请求,也可以响应隐式Intent请求在通过和intent-filter比较来解析隐式Intent请求时,Android将以下三个因素作为选择的参考标准

 Action

 Data

 Category

而Entra和Flag在解析收到Intent时是并不起作用的

Intent Filter

  应用程序的组件为了告诉Android自己能响应、处理哪些隐式Intent请求,可以声明一个甚至多个intent-filter每个Intent Filter描述该组件所能响应Intent请求的能力——组件希望接收什么类型的请求行为,什么类型的请求数据

如何为组件声明自己的IntentFilter? 常见的方法是在AndroidManifest.xml文件中用属性<Intent-Filter>描述组件的Intent Filter

 前面我们提到,隐式Intent和IntentFilter进行比较时的三要素是Intent的动作、数据以及类别实际上,一个隐式Intent请求要能够传递给目标组件,必要通过这三个方面的检查。如果任何一方面不匹配,Android都不会将该隐式Intent传递给目标组件接下来我们讲解这三方面检查的具体规则。

1.动作测试

<intent-filter>元素中可以包括子元素<action>,比如:

<intent-filter>

<actionandroid:name="com.example.project.SHOW_CURRENT" />

<actionandroid:name="com.example.project.SHOW_RECENT" />

<actionandroid:name="com.example.project.SHOW_PENDING" />

</intent-filter>

一条<intent-filter>元素至少应该包含一个<action>,否则任何Intent请求都不能和该<intent-filter>匹配

如果Intent请求的Action和<intent-filter>中个某一条<action>匹配,那么该Intent就通过了这条<intent-filter>的动作测试

如果Intent请求或<intent-filter>中没有说明具体的Action类型,那么会出现下面两种情况

(1) 如果<intent-filter>中没有包含任何Action类型,那么无论什么Intent请求都无法和这条<intent-filter>匹配;

(2) 反之,如果Intent请求中没有设定Action类型,那么只要<intent-filter>中包含有Action类型,这个Intent请求就将顺利地通过<intent-filter>的行为测试

2.类别测试

<intent-filter>元素可以包含<category>子元素,比如:

<intent-filter. . . >

     <categoryandroid:name="android.Intent.Category.DEFAULT" />

     <categoryandroid:name="android.Intent.Category.BROWSABLE" />

</intent-filter>

只有当Intent请求中所有的Category与组件中某一个Intent-filter的<category>完全匹配时,才会让该Intent请求通过测试,Intent-filter中多余的<category>声明并不会导致匹配失败

android.intent.category.DEFAULT的作用

每一个通过 startActivity()方法发出的隐式 Intent 都至少有一个 category,就是 "android.intent.category.DEFAULT",所以只要是想接收一个隐式Intent 的 Activity 都应该包括 "android.intent.category.DEFAULT" category,不然将导致 Intent匹配失败

3.数据测试

数据在<intent-filter>中的描述如下:

<intent-filter. . . >

     <data android:type="video/mpeg" android:scheme="http". . . />

     <data android:type="audio/mpeg" android:scheme="http". . . />

</intent-filter>

<data>元素指定了希望接受的Intent请求的数据URI和数据类型,URI被分成三部分来进行匹配:schemeauthority和path其中,用setData()设定的Intent请求的URI数据类型和scheme必须与Intent-filter中所指定的一致若Intent-filter中还指定了authority或path,它们也需要相匹配才会通过测试

每个数据<data>元素可以指定一个URI和一个数据类型(MIME媒体类型)有一些单独的属性-模式,主机,端口和路径-URI的每个部分:

scheme://host:port/path

content://com.example.project:200/folder/subfolder/etc

模式是"内容",主机是"com.example.project",端口是"200",路经是"folder/subfolder/etc"主机和端口一起组成URI鉴权(authority);如果未指定主机,端口会被忽略。

这些属性都是可选的,但彼此有依赖关系:一个授权要有意义,必须指定一个模式一个路经要有意义,必须同时指定模式和鉴权。

当一个意图对象中的URI被用来和一个过滤器中的URI规格比较时,它实际上比较的是上面提到的URI的各个部分比如,如果过滤器仅指定了一个模式,所有那个模式的URIs和这个过滤器相匹配;如果过滤器指定了一个模式、鉴权但没有路经,所有相同模式和鉴权的URIs可以匹配上,而不管它们的路经;如果过滤器指定了一个模式鉴权和路经,只有相同模式、鉴权和路经的URIs可以匹配上当然,一个过滤器中的路径规格可以包含通配符,这样只需要部分匹配即可。

数据<data>元素的类型属性指定了数据的MIME类型这在过滤器里比在URI里更为常见。意图对象和过滤器都可以使用一个"*"通配符指定子类型字段-比如,"text/*"或者"audio/*"-指示任何匹配的子类型

数据测试同时比较意图对象和过滤器中指定的URI和数据类型。规则如下:

a. 一个既不包含URI也不包含数据类型的意图对象仅在过滤器也同样没有指定任何URIs和数据类型的情况下才能通过测试

b .一个包含URI但没有数据类型的意图对象仅在它的URI和一个同样没有指定数据类型的过滤器里的URI匹配时才能通过测试这通常发生在类似于mailto:和tel:这样的URIs上:它们并不引用实际数据。

c. 一个包含数据类型但不包含URI的意图对象仅在这个Intent-filter列举了同样的数据类型而且也没有指定一个URI的情况下才能通过测试

d. 一个同时包含URI和数据类型(或者可从URI推断出数据类型)的意图对象可以通过测试,如果它的类型和过滤器中列举的类型相匹配的话如果它的URI和这个过滤器中的一个URI相匹配或者它有一个内容content:或者文件file: URI而且这个过滤器没有指定一个URI,那么它也能通过测试换句话说,一个组件被假定为支持content:和file: 数据如果它的过滤器仅列举了一个数据类型。(这个是网上copy过来,)

PS:如果一个意图可以通过不止一个活动或服务的过滤器,用户可能会被询问要激活那个组件这个倒是经常遇到。例如,当你手机上安装多个浏览器的时候,调用浏览器,回弹出对话框,询问你到底启动哪一个应用。如果没有发现目标对象将会出现异常

===================================================================================

       当自己创建日志后,在NotesList中会显示出来。当点击这个ListView中的某一个item时,响应的代码为:

         @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        Uri noteUri = ContentUris.withAppendedId(getIntent().getData(), id);
        Log.e(TAG," onListItemClick() called,noteUri = " + noteUri);

String action = getIntent().getAction();
        if (Intent.ACTION_PICK.equals(action) || Intent.ACTION_GET_CONTENT.equals(action)) {
            // The caller is waiting for us to return a note selected by
            // the user.  The have clicked on one, so return it now.
            setResult(RESULT_OK, new Intent().setData(noteUri));
        } else {
            // Launch activity to view/edit the currently selected item
            startActivity(new Intent(Intent.ACTION_EDIT, noteUri));
        }
    }

    其中,代码startActivity(new Intent(Intent.ACTION_EDIT, noteUri));就是启动一个隐式的Activity此时,Android系统就出来接管这个隐式的Intent,它会根据intent-filter来匹配将要启动的组件显然,此时的Intent中的action 为:android.intent.action.EDIT此时,从AndroidManifest.xml文件中进行分析,发现文件中有两个组件intent-filter中均包含<action android:name="android.intent.action.EDIT" />,

 <activity android:name="NotesList"
            android:label="@string/title_notes_list">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.EDIT" />
                <action android:name="android.intent.action.PICK" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
            </intent-filter>
          ... ... ... ...
        </activity>

<activity android:name="NoteEditor"
            android:theme="@android:style/Theme.Light"
            android:configChanges="keyboardHidden|orientation">
            <!-- This filter says that we can view or edit the data of
                 a single note -->
            <intent-filter android:label="@string/resolve_edit">
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.EDIT" />
                <action android:name="com.android.notes.action.EDIT_NOTE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
            </intent-filter>
        ... ... ... ...
        </activity>

          为神马NotesList组件中的intent-filter也包涵 一个<action android:name="android.intent.action.EDIT" />?估计是Google的程序员在copy代码时,出现的差错这个action本没有作用。

       那么NoteEditor组件是如何正确的被启动的呢??

          在上一篇博文里面谈到,在通过和intent-filter比较来解析隐式Intent请求时,Android将以下三个因素作为选择的参考标准 Action,Data,Category。当Action和Category一致时,只能从Date来进行区分咯!

        于是,intent-filter中的<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />和 <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />开始浮出水面了这两个date是神马区别呢?

       在NotesList.java中的代码,添加Log信息

          @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        Uri noteUri = ContentUris.withAppendedId(getIntent().getData(), id);
        Log.e(TAG," onListItemClick() called,noteUri = " + noteUri);

        ... ... ... ...

       打印的Log信息如下:

      NotesList(4103):  onListItemClick() called,noteUri = content://com.example.notepad.provider.NotePad/notes/2

     Android系统获取到这个Intent后,去掉开始的content:标识和后面的资源路径(/notes)以及资源ID(2)得到com.google.provider.NotePad,然后就加载这个content provider紧接着,就开始通过NotePadProvider.java中的getType(Uri),返回数据的MIME类型,其返回值为 vnd.android.cursor.item/vnd.google.note,这个就与组件NoteEditor匹配上了

posted @ 2013-02-14 19:23  __木头鱼__  阅读(627)  评论(0编辑  收藏  举报