代码改变世界

Anroid中的Intent

2009-04-22 14:40  cppguy  阅读(888)  评论(0编辑  收藏  举报

1:什么是 Intent

Intent是对被执行操作的抽象描述。调用 startActivity(Intent),可以启动 Activity;调用 broadcastIntent(Intent),可以把 Intent 发送给任何相关的 IntentReceiver 组件;调用 startService(Intent, Bundle) 以及 bindService(Intent, String, ServiceConnection, int) 可以让应用和后台服务进行通信。

Intent 提供了一个在不同应用的代码之间进行晚绑定 (late runtime binding) 的机制。它主要被用来启动 Activities,因此可以被看作是 Activities 之间的粘合剂。Intent 大体上是一个被动数据结构,该数据结构包括被执行动作的抽象描述。Intent 中的主要内容有:
ü         action -- 需要被执行的动作。比如 VIEW_ACTION, EDIT_ACTION, MAIN_ACTION 等。
ü         data -- 执行动作要操作的数据,在 Intent 里用指向数据记录的URI (ContentURI) 表示。比如联系人数据库中的一个联系人记录。
译注:被动数据结构:只能由外部线程或者进程改变的数据结构。与能够通过相关的线程或者进程执行内部操作从而产生外部行为的主动数据结构相对应。
下面是一些 action/data 对的例子:
ü         VIEW_ACTION content://contacts/1 -- 显示标识符为"1"的联系人的信息。
ü         EDIT_ACTION content://contacts/1 -- 编辑标识符为"1"的联系人的信息。
ü         VIEW_ACTION content://contacts/ -- 显示可遍历的联系人列表。这是用来进入联系人应用主界面(顶级入口,top-level entry)的典型方法。在这个界面中察看某个联系人会产生一个新的 Intent:{VIEW_ACTION content://contacts/N},用来启动新的Activity,显示该联系人的详细信息。
ü         PICK_ACTION content://contacts/ -- 先是可遍历的联系人列表,并且允许用户在列表中选择一个联系人,然后把这个联系人返回给"上级活动"(parent activity)。例如:电子邮件客户端可以使用这个 Intent,要求用户在联系人列表中选择一个联系人。
除了 action, data 两个主要属性,Intent 还具有一些其它属性,这些属性也可以被用在Intent 里:
category -- 类别,被执行动作的附加信息。例如 LAUNCHER_CATEGORY 表示Intent 的接受者应该在 Launcher 中作为顶级应用出现;而 ALTERNATIVE_CATEGORY 表示当前的 Intent 是一系列的可选动作中的一个,这些动作可以在同一块数据上执行。
type -- 数据类型,显式指定 Intent 的数据类型 (MIME)。一般上 Intent 的数据类型能够根据数据本身进行判定,但是通过设置这个属性,可以强制采用显式指定的类型而不再进行推导。
component -- 组件,为使用 Intent 的组件类指定名称。通常会根据 Intent 中包含的其它信息 —— 比如 action, data/type, categories —— 进行查找,最终找到一个与之匹配的组件。如果这个属性存在的话,将直接使用它指定的组件,不再执行上述查找过程。指定了这个属性以后,Intent 的其它所有属性都是可选的。
extras -- 额外的附加信息,是其它所有附加信息的集合。使用 extras 可以为组件提供扩展信息,比如,如果要发送电子邮件,也就是要执行“发送电子邮件”的动作,可以将电子邮件的标题、正文等保存在 extras 里。
在 Intent 类里定义了多种标准 action 和 category 常量(字符串),同时应用也可以根据自己的需要进行定义。这些字符串使用 JAVA 风格的 scoping,从而保证它们的唯一性。比如标准 VIEW_ACTION 的定义是 “android.app.action.VIEW”。
概括而言,“动作”、“数据类型”、“类别”(译注:Intent的action类 型)和“附加数据”一起形成了一种语言。这种语言使得系统能够理解诸如“打john的手机”之类的短语。随着应用不断的加入到系统中,它们可以添加新的“ 动作”、“数据类型”、“类别”来扩展这种语言。应用也可以提供自己的 activities 来处理已经存在的“短语”,从而改变这些“短语”的行为。
 
 2解析Intent
Intent 有两种主要形式:
ü         显式意图(直接意图?)。显式意图是指定了 component 属性的 intents调 用 setComponent(ComponentName) 或者 setClass(Context, Class) 可以为 intents 设定 component 属性 —— 指定具体的组件类。这些 intents 一般不包括包括其它任何信息,它们通常只是用来通知应用启动内部的 activities 作为该应用的(当前)用户界面。
ü         隐式意图(含蓄意图?)。隐式意图是没有指明 comonent 的 intents。这些 intents 必须包括足够的信息,这样系统才能确定在所有的可用组件中,对一个 intent 来说运行哪一个组件才是最合适的。
在使用 implicit intents 的时候,对于一个任意的 intent,我们需要知道用它来做什么。“Intent 解析过程”用来处理这个问题。“Intent 解析过程”将 intent 映射到可以处理它的 activity, IntentReceiver 或者 service。
Intent 解析机制主要是将已安装应用程序包里的 Intent-Filter 描述和 Intent 进行匹配。如果使用广播发送 Intent,还要在已经注册的 IntentReceiver 中尽心匹配。更多的相关描述可以在 IntentFilter 中找到。
在解析 Intent 的过程中要用到 Intent 的三个属性:动作、数据类型和类别。使用这些属性,就可以 PackageManager 上查询能够处理当前 intent 的合适组件。组件是否合适由 AndroidManifest.xml 文件中提供的 intent 信息决定。判断的方法如下:
1、如果 intent 指明了要执行的 action,组件 action 列表中就必须包含着个 action,否则不能匹配;
2、如果 Intent 没有提供数据类型 (type),系统从数据 (data) 中得到数据类型。和 action 一样,组件的数据类型列表中必须包含 intent 的数据类型,否则不能匹配。
3、如果 Intent 中的数据不是 content: 类型的 URL,而且 Intent 也没有明确指定它的数据类型,将根据 Intent 中数据的 scheme (比如 http: or mailto:) 进行匹配。同上,Intent 的 scheme 必须出现在组件的 scheme 列表中。
4、如果 Intent 指定了一个或多个类别,这些类别必须全部出现在组建的类别列表中。比如 intent 中包含了两个类别:LAUNCHER_CATEGORY 和 ALTERNATIVE_CATEGORY,解析得到的组件必须至少包含这两个类别。
以一个应用实例作为例子,这个应用可以让用户浏览便笺列表、查看每一个便笺的详细信息:
      package="com.google.android.notepad">
    <application android:icon="@drawable/app_notes"
            android:label="@string/app_name">
        <provider class="NotePadProvider"
                android:authorities="com.google.provider.NotePad" />
               
        <activity class=".NotesList" android:label="@string/title_notes_list">
            <intent-filter>
                <action android:value="android.intent.action.MAIN" />
                <category android:value="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:value="android.intent.action.VIEW" />
                <action android:value="android.intent.action.EDIT" />
                <action android:value="android.intent.action.PICK" />
                <category android:value="android.intent.category.DEFAULT" />
                <type android:value="vnd.android.cursor.dir/vnd.google.note" />
            </intent-filter>
            <intent-filter>
                <action android:value="android.intent.action.GET_CONTENT" />
                <category android:value="android.intent.category.DEFAULT" />
                <type android:value="vnd.android.cursor.item/vnd.google.note" />
            </intent-filter>
        </activity>
        <activity class=".NoteEditor" android:label="@string/title_note">
            <intent-filter android:label="@string/resolve_edit">
                <action android:value="android.intent.action.VIEW" />
                <action android:value="android.intent.action.EDIT" />
                <category android:value="android.intent.category.DEFAULT" />
                <type android:value="vnd.android.cursor.item/vnd.google.note" />
            </intent-filter>
            <intent-filter>
                <action android:value="android.intent.action.INSERT" />
                <category android:value="android.intent.category.DEFAULT" />
                <type android:value="vnd.android.cursor.dir/vnd.google.note" />
            </intent-filter>
        </activity>
        <activity class=".TitleEditor" android:label="@string/title_edit_title"
                android:theme="@android:style/Theme.Dialog">
            <intent-filter android:label="@string/resolve_title">
                <action android:value="com.google.android.notepad.action.EDIT_TITLE" />
                <category android:value="android.intent.category.DEFAULT" />
                <category android:value="android.intent.category.ALTERNATIVE" />
                <category android:value="android.intent.category.SELECTED_ALTERNATIVE" />
                <type android:value="vnd.android.cursor.item/vnd.google.note" />
            </intent-filter>
        </activity>
    </application>
</manifest>
 
例子中的第一个 activity 是 com.google.android.notepad.NotesList。它是进入应用的主入口(main entry),具有三种功能,分别由三个 intent 模板进行描述。
1、第一个功能是进入便笺应用的顶级入口。它的类型是 android.app.category.LAUNCHER,说明这个应用应该在 Launcher 中被列出。
2、第二个功能用来浏览可用的便笺,或者让用户选择一个特定的便笺并且把这个便 笺返回给调用者。当数据类型是 vnd.android.cursor.dir/vnd.google.note (便笺记录的目录) 的时候,执行动作 android.app.action.VIEW 可以浏览可用的便笺;执行动作 android.app.action.PICK 可以让用户选择便笺。
3、第三个功能返回给调用者一个用户选择的便笺。当数据类型是 vnd.android.cursor.dir/vnd.google.note 的时候,执行动作 android.app.action.GET_COUTENT 调用者不需要知道
有了这些功能,就能够将下列 intents 匹配到 NotesList 的 activity:
{ action=android.app.action.MAIN }. 如果 activities 能够被用作进入应用的顶级入口,就可以和这个 intent 进行匹配。
{ action=android.app.action.MAIN, category=android.app.category.LAUNCHER }. 这是目前 Launcher 实际使用的 intent,构成了它的顶级列表。
问题:怎么构成??
{ action=android.app.action.VIEW data=content://com.google.provider.NotePad/notes }. 显示 "content://com.google.provider.NotePad/notes" 下所有便笺的列表,用户可以遍历这个列表,并且察看便笺的详情。
{ action=android.app.action.PICK data=content://com.google.provider.NotePad/notes }. 让用户在 "content://com.google.provider.NotePad/notes" 之下的便笺列表中选择一个,然后将这个便笺的 URL 返回给调用者。
{ action=android.app.action.GET_CONTENT type=vnd.android.cursor.item/vnd.google.note }. 这个 intent 和上面的 pick 动作类似,不同的是这个 intent 允许调用者(仅仅)指定它们需要的数据类型(,而不需要了解数据存放的详细位置,即数据的 URI)。系统根据这个数据类型选择恰当的 activity,然后让用户选择某些数据。
 
第二个 activity 是 com.google.android.notepad.NoteEditor,它为用户显示一个单独的便笺,并且允许用户对这个便笺进行修改。它具有两个 intent 模板,所以具有两个功能。第 一个操作是主要的操作,允许用户察看和编辑一个便签(执行 android.app.action.VIEW 和 android.app.action.EDIT 动作,数据类型是 vnd.android.cursor.item/vnd.google.note)。第二个模板可以让调用者显示创建新便笺的用户界面,并且将新便笺插 入到便笺列表中(执行 android.app.action.INSERT 动作,数据类型是 vnd.android.cursor.dir/vnd.google.note)。
有了这两个功能,下列 intents 就能够匹配到 NotesList 的 activity:
{ action=android.app.action.VIEW data=content://com.google.provider.NotePad/notes/{ID} } 向用户显示标识为 ID 的便笺。将标识为 ID 的便笺缩写为 note{ID},下同。
{ action=android.app.action.EDIT data=content://com.google.provider.NotePad/notes/{ID} } 让用户能够编辑 notes{ID}。
{ action=android.app.action.INSERT data=content://com.google.provider.NotePad/notes } 创建一个新的便笺,新便笺被创建在“content://com.google.provider.NotePad/notes”所表示的便笺列表中。用 户可以编辑这个便签。当用户保存这个便笺后,这个新便笺的 URI 将会返回给调用者。
 
最后一个 activity 是 com.google.android.notepad.TitleEditor,它可以让用户编辑便笺的标题。它可以被实现为一个类,在 intent 中明确设定 component 属性后,应用可以直接调用这个类;不过在这里我们展示的是如何在已有数据上发布可选操作。这个 activity 只有一个单独的 intent 模板,它具有一个私有 action: com.google.android.notepad.action.EDIT_TITLE,允许用户编辑便笺的标题。和前面的 view 和 edit 动作一样,调用这个 intent 的时候,也必须指定具体的便笺。不一样的是,这里显示和编辑的只是便笺数据中的标题。
除了支持确省类别 (default category, android.intent.category.DEFAULT,原文是 android.intent.category.VIEW,有误),标题编辑器还支持另外两个标准类 别:android.intent.category.ALTERNATIVE 和 android.intent.category.SELECTED_ALTERNATIVE。实现了这两个类别之后,其它 activities 可以调用函数 queryIntentActivityOptions(ComponentName, Intent[], Intent, int) 查询这个 activity 支持的 actions,而不需要了解它的具体实现;或者调用 addIntentOptions(int, int, ComponentName, Intent[], Intent, int, Menu.Item[]) 建立动态菜单。需要说明的是,这个 intent 模板有一个明确的名称(通过 android:label="@string/resolve_title" 指定)。在用户浏览数据的时候,如果这个 activity 是数据的一个可选操作,指定明确的名称可以为用户提供一个更好控制界面。 有了这个功能,下列 intents 就能够匹配到 NotesList activity    {action=com.google.android.notepad.action.EDIT_TITLE data=content://com.google.provider.NotePad/notes/{ID} } 显示并且允许用户编辑 note{ID} 的标题。