android基础知识06:intent和intentfilter

        本文主要介绍intent和intentfilter的相关概念及应用,所有资料来源于网络。

       主要参考资料:《 Android中的Intent详细讲解》《Intent 和 Intent Filter》《Android开发之旅: Intents和Intent Filters(理论部分)》


1、intent的概念和作用

1.1 笼统的理解

        在一个Android应用中,主要是由四种组件组成的,这四种组件可参考“Android应用的构成”。而这四种组件是独立的,它们之间可以互相调用,协调工作,最终组成一个真正的Android应用。在这些组件之间的通讯中,主要是由Intent协助完成的。
        Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给调用的组件,并完成组件的调用。因此,Intent在这里起着一个媒体中介的作用,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦。
        例如,在一个联系人维护的应用中,当我们在一个联系人列表屏幕(假设对应的Activity为listActivity)上,点击某个联系人后,希望能够跳出此联系人的详细信息屏幕(假设对应的Activity为detailActivity)为了实现这个目的,listActivity需要构造一个 Intent,这个Intent用于告诉系统,我们要做“查看”动作,此动作对应的查看对象是“某联系人”,然后调用startActivity (Intent intent),将构造的Intent传入,系统会根据此Intent中的描述,到ManiFest中找到满足此Intent要求的Activity,系统会调用找到的 Activity,即为detailActivity,最终传入Intent,detailActivity则会根据此Intent中的描述,执行相应的操作。


1.2 谷歌api中的描述

        Android 应用程序中有三大核心组件: Activity, Service, Broadcast Receiver 都是通过被称之为意图的消息运行。Intent messaging is a facility for late run-time binding between components in the same or different applications. 意图本身一个 Intent 对象,它保存了对要执行操作的抽象描述—对于broadcasts来说,则表示对已经发生并且正要报告的操作。对这下三种组件,发送intents分别有不同的机制。
        传递一个Intent对象到 Context.startActivity(intent) 或者 Activity.startActivity ForResult(int) 去运行一个Activity(可以在通过此方式启动后的Activity中调用 Activity.setResult() 设置结果参数,该参数将会在启动当前activity的activity中被接收---可以通过onActivityResult(int requestCode, int resultCode, Intent data) 接收)
        传递一个Intent对象到 Context.startService(intent) 去启动一个service 或者 传递一个新的指令到正在运行的service中。另外,还可以通过 Context.bindService(intent) 去绑定一个Service。(在调用组件和目标Service 建立一个连接)
        传递一个Intent对象到 任何一个broadcast methods (如: Context.sendBroadcast() , Context.sendOrderedBroadcast(),Context.sendStickyBroadcast() ) 该intent将被传递给所有已经被注册的broadcast receiver中。
         在以上的三种情况下,当Intent被传递出后,Android系统会找到适合的activity,service,或者是多个broadcast receiver去响应这个intent。,这三种情况不会存在重叠的部分,它们相互独立,互不干扰。(调用Context.startActivity()后 intent只会被相应的activity接收到)


2、intent类

        一个Intent对象是一个信息包。它包含了要接收此Intent的组件需要的信息(例如需要的动作和动作需要的信息)和 android 系统需要的信息(要处理此Intent的组件的类别和怎样启动它)
        总的来说,Intent Object 主要包括以下信息。

2.1 Component name 

        处理Intent 的组件名称。此字段是一个 ComponentName object---它是目标的组件的完整限定名(包名+类名) 例如: “com.android,.test.TestActivity” .
        该字段是可选的。如果设置了此字段,那么 Intent Object 将会被传递到这个组件名所对应的类的实例中。 如果没有设置,Android 会用 Intent object 中的其它信息去定位到一个合适的目标组件中。 (称之为 : Intent 解析。。。这个稍后会讲到)
        设置Component name 可以通过 setComponent() , setClass() 或者 setClassName()进行设置。 可以通过 getComponent() 进行读取

2.2 动作(Action)

        一个字符串,代表要执行的动作。 -- 或者,对于 broadcase intents 来说,表示正在发生,并且被报告的动作。Intent 类中 定义了许多动作常量。 如下:


        你也可以定义自己的 action strings 来激活组件。自定义的action 应该包含包名作为前缀: 例如"com.example.project.SHOW_COLOR".
        Action 很大程度上决定 Intent余下部分的结构。 ---- 特别是:data 和 extras 两个字段。就像一个方法的方法名通常决定了方法的参数和返回值。 基于这个原因,应该给action 命名一个尽可能明确的名字。 可以通过 setAction() 设置action,通过 getAction() 进行获取.

2.3 Data

        Data属性有两部分构成: 数据URI 和 数据MIME type 。 action的定义往往决定了data该如何定义。 例如: 如果 一个Intent的 action 为 ACTION_EDIT 那么它对应的data 应该包含待编辑的数据的URI . 如果一个action 为:ACTION_CALL ,那么data 应该为 tel: 电话号码的URI . 类似的, 如果action 为 ACTION_VIEW 那么data 应该为: http: URI , 接收到的activity 将会下载并显示相应的数据。
        当一个Intent 和 有能力处理此Intent的组件进行匹配时, 除了 data的URI以外,了解data的类型(MIME Type)也很重要。 例如: 一个显示图片的组件 不应该去播放声音文件。
        许多情况下,data type 可以从URI中推测出。 尤其是: URI = content: URIs这时候数据通常是位于本设备上而且是由某个content provider来控制的。即便如此,我们仍然可以明确的在 Intent object上设置一个 data type. setData() 方法只能设置URI, setType() 设置MIME type, setDataAndType() 可以对二者都进行设置, 获取URI 和 data type 可分别调用 getData() 和 getType() 方法。

2.4 Category

        一个字符串, 包含了处理该Intent的组件的种类信息, 起着对action的补充说明作用.
        一个Intent对象可以有任意多个 category。和action 一样, 在Intent class 中也定义了几个 category 常量。。 如下:


addCategory() 添加一个 category
removeCategory() 删除一个 category()
getCategorys() 获取所有的category()

2.5 Extras

        为键-值对形式的附加信息. 例如ACTION_TIMEZONE_CHANGED的intent有一个"time-zone"附加信息来指明新的时区, 而ACTION_HEADSET_PLUG有一个"state"附加信息来指示耳机是被插入还是被拔出.
        intent对象有一系列put...()和set...()方法来设定和获取附加信息. 这些方法和Bundle对象很像. 事实上附加信息可以使用putExtras()和getExtras()作为Bundle来读和写.

2.6 Flags

        有各种各样的标志,许多指示Android系统如何去启动一个活动(例如,活动应该属于那个任务)和启动之后如何对待它(例如,它是否属于最近的活动列表)。所有这些标志都定义在Intent类中。

3、intent解析

intent的投递,有两种方式:

  •       显式的设定目标组件的component名称。不过有时开发者不知道其他应用的component名称。显式方式常用于自己应用内部的消息传递,比如应用中一个activity启动一个相关的service或者启动一个姊妹activity;
  •  隐式intent,component名称为空的情况。这种方式往往用于激活其他应用中的组件。
      android投递一个显式的intent,只需找到对应名称的组件即可。
      隐式的intent需要用到不同的策略。android需要找到处理这个intent的最合适组件(集合)。要通过intent filter,比较intent对象和组件关联结构。filter根据组件的能力决定他们能处理哪些intent。android系统打开合适的组件处理相应的隐式intent。如果组件不包含任何intent filter,那只能接收显式的intent。带filter的组件既可接收隐式intent也可接收显式的。
        Intent有三个方面可用于intent filter:
                action
                data,包括URI部分和数据类型部分
                category
        extra和flag在这方面不起作用。

4、intentfilter

        为了能支持隐式intent,activity、service和broadcast receiver会包含1到多个intent filter。每个intent filter描述组件的可接收一组intent的能力。在intent filter中,说明了可接受的类型,以及不想要的intent。隐式的intent要想投递到一个组件,只需通过组件的一个filter即可。
        组件把filter分成多个,是为了针对具体不同的任务。在sample中的Note pad示例中,NoteEditor activity有两个filter,一个用于启动并打开指定的note,另一个是为了打开新的空的note。
        一个intent filter是一个IntentFilter类的实例。但是,android系统必须在组件未启动的情况下就知道它的能力,因此intent filter一般不会在java代码中设置,而是在应用的manifest文件中作为元素的方式声明。一个例外是,为broadcast receiver注册动态的filter,可以调用Context.registerReceiver()方法,通过直接实例化IntentFilter对象创建。
        filter有三个平等的部分:action、data和category。隐式intent将测试这三个部分。一个intent要想投递到一个组件,那么这三个测试都要通过才行。当然如果组件有多个intent filter,可能一个intent没有通过,但是通过了另外的一个,这样也可以把intent投递到组件。

4.1 intentfilter过滤规则

       intentfilter的过滤规则可以由如下图表示。


        需要注意的是:如果一个Intent 可以通过多个activity或者filter的filter,那么用户将会被询问需要激活哪个组件。 如果一个都没有的话,将会抛出异常。可以在Intent Filter的节点里添加一个可选的标签。最高等级的组件会返回。  

4.2 action测试


    
    
    
    . . .
        如实例所示,当一个intent对象只能命名一个单一的action,一个过滤器则可以列出多个action。这个列表也可以是空的, 一个过滤器必须包含一个 element ,否则它将阻止所有的intents要通过这个测试,在intent被指定的action必须匹配在过滤器中所列的action的其中之一。如果一个intent对象或者过滤器没有指定action。 结果如下 :

  • 如果一个filter 没有指定任何action ,那么则没有任何intent会被匹配。所以,所有的intent将不会通过此测试。
  • 另一方面,如果一个intent对象没有指定任何action,那么将自动通过此测试—只要这个过滤器中有至少一个action.

4.3 Category测试

 
 
 
    . . . 
        要通过category测试, Intent对象中包含的每个category必须匹配filter中的一个。Filter可以列出额外的category,但是不能漏掉 intent 对象包含的任意一个category。
        原则上,一个没有任何categorys的 Intent object 将总是通过此测试。大多数情况下是正确的。然而,也有例外,android对待所有传入 startActivity() 中的隐式视图,都认为它们至少包含了一个 category --- "android.intent.category.DEFAULT". . 因此,希望接收这些隐式意图的activities必须在在它们的 intent filters 中包含”android.intent.category.DEFAULT” ..有(对于含"android.intent.action.MAIN" and "android.intent.category.LAUNCHER"的filter 则是例外。因为它们标记了此activity开启了一个新的task 和 将出现在 auncher screen。它们也可以包含“com.intent.category.DEFAULT”,但没必要)

4.4 Data

        类似于action, categories, data也是 intent filter 中的一个子节点, 可以设置多个 data节点,也可以一个不设置。

 
 
 
    . . . 
        每个< data > 元素可以指定一个 URI 和 一个 data type (MIME media type) . URI 有以下几个属性组成 : schema,host,port,path
Schema://host:port/path
例如:
content://com.example.project:200/folder/subfolder/etc
在上例中 schema 是 content: host: com.example.project
Port: 200 Path: folder/subfolder/etc
主机 host 和 port 一起组成了URI authority,如果没有指定 host,那么port将被忽略。
        节点中的属性都是可选的,但它们并非相互独立。要使一个authority 有意义,必须要指定 scheme 。 要是 path 有意义, scheme 和 authority(host:port) 必须指定。
        当Intent对象中的URI 和 intent filter 进行比较时,它只会进行部门比较。 例如: 如果一个 filter 只指定了一个scheme , 那么所有包含该scheme的URI都会匹配。 如果一个filter只指定了 scheme 和 authority ,没有path, 那么所有包含此scheme 和 authority 将会匹配。如果一个filter指定了一个scheme,authority, 和一个path, 那么只有包含同样的 scheme,authoritym,path会匹配。 但是,对于path,我们可以使用通配符进行部门匹配。
        节点的 type 属性指定了 data的MIME type。 它比在filter中的URI 更常见 intent对象和filter都可以使用 “*” 通配符作为子类型 – 例如: "text/*" or "audio/*"--- 表示所有子类型都匹配。
        data test 会将 intent对象中的URI 和 data type 与filter指定的都进行比较。 规则如下:
a) 如果一个intent 没有指定URI 和 data type , 那么如果filter中也是同样,则通过测试。
b) 如果一个iintent 有URI 但是没有 data type(或者是data type不能从uri中推断出来 ) 只能通过这样的filter: uri匹配, 并且不指定类型. 这种情况限于类似mailto:和tel:这样的不指定实际数据的uri.
c) 如果一个intent 包含 data type 但是没有 uri ,那么 filter中列出相同的data type 并且没有指定URI 则通过测试。
d) 如果一个intent包含一个URI 和data type (或者data type 可以从URI中推断出来),那么filter列出的有相同data type ,intent对象的uri要么和filter中的uri匹配,要么intent的uri为 content: or file: 并且filter不指定uri
        如果一个Intent 可以通过多个activity或者filter的filter,那么用户将会被询问需要激活哪个组件。 如果一个都没有的话,将会抛出异常。

5、Common cases

        这个规则是针对 data test 中的规则d) ,它反映出组件可以从一个file或者content provider 获取本地数据。因此,filters 可以是设置data type并且没有必要明确的将 scheme 命名为 content: 和 file: 。
        下面的 元素,告诉android该组件可以从content provider中获取image data 并显示她。

        由于大部分可用的数据都是由content provider提供, 指定数据类型但不指定uri的filter是最常见的情况.
Another common configuration is filters with a scheme and a data type. For example, a element like the following tells Android that the component can get video data from the network and display it:
        设置了 scheme 和 data type是 另一个比较常见的配置是 。下面的 元素,告诉android该组件可以从网上获取video并显示

        考虑当用户在一个web page上点了一个链接后,浏览器应用程序做了什么。 它首先会试图去显示该数据(当做一个html页来处理)。如果它不能显示此数据,它会使用一个设置 scheme 和 data type 的隐式意图 去启动一个能显示此数据的activity。如果没有找到接受者,它会调用下载管理器去下载该数据,然后将其放在content provider的控制之下,这样很多activitys (那些之命名了datatype)可以处理该数据
大部分应用程序还有一种方式可以单独启动,不用去引用特别的数据。那些要启动应用程序的activity 必须 设置 "android.intent.action.MAIN" 作为action。
        如果还要显示在程序启动器上则必须设置 "android.intent.category.LAUNCHER" 为 category.

 
 
 

      在这里,需要注意一下,在content provider类中,有一个函数getType(Uri uri),这个类是跟处理uri有关系的。一般的逻辑是通过URI获得mimeType的值,然后再通过intentfilter的data属性去找。

6、使用intent匹配

      Intents对照着Intent过滤器匹配,不仅去发现一个目标组件去激活,而且去发现设备上的组件的其他信息。例如,Android系统填充应用程序启动列表,最高层屏幕显示用户能够启动的应用程序:是通过查找所有的包含指定了"android.intent.action.MAIN"的动作和"android.intent.category.LAUNCHER"种类的过滤器的活动,然后在启动列表中显示这些活动的图标和标签。类似的,它通过查找有"android.intent.category.HOME"过滤器的活动发掘主菜单。
      我们的应用程序也可以类似的使用这种Intent匹配方式。PackageManager有一组query…()方法返回能够接收特定intent的所有组件,一组resolve…()方法决定最适合的组件响应intent。例如,queryIntentActivities()返回一组能够给执行指定的intent参数的所有活动,类似的queryIntentServices()返回一组服务。这两个方法都不激活组件,它们仅列出所有能够响应的组件。对应广播接收者也有类似的方法——queryBroadcastReceivers()。

7、其他

        一般的文章在讲到category时,都会漏了两个类别:ALTERNATIVE,SELECTED_ALTERNATIVE。

       ALTERNATIVE:一个Intent Filter的用途是使用动作来帮忙填入上下文菜单。ALTERNATIVE种类指定,在某种数据类型的项目上可以替代默认执行的动作。例如,一个联系人的默认动作时浏览它,替代的可能是去编辑或删除它。
       SELECTED_ALTERNATIVE:与ALTERNATIVE类似,但ALTERNATIVE总是使用下面所述的Intent解析来指向单一的动作。SELECTED_ALTERNATIVE在需要一个可能性列表时使用。

      除了支持缺省类别(android.intent.category.DEFAULT),标题编辑器还支持另外两个标准类别:android.intent.category.ALTERNATIVE和android.intent.category.SELECTED_ALTERNATIVE。
     实现了这两个类别之后,其它 Activity就可以调用queryIntentActivityOptions(ComponentName, Intent[], Intent, int)查询这个Activity提供的action,而不需要了解它的具体实现;或者调用addIntentOptions(int, int, ComponentName, Intent[], Intent, int, Menu.Item[])建立动态菜单。需要说明的是,在这个intent-filter中有一个明确的名称(通过android:label= "@string/resolve_title"指定),在用户浏览数据的时候,如果这个Activity是数据的一个可选操作,指定明确的名称可以为用户提供一个更好控制界面。

     例子:


     
       
       
       
       
       
      
    

        在这个 Activity的唯一的intent-filter中,拥有一个私有的action: com.google.android.notepad.action.EDIT_TITLE,表明允许用户编辑便笺的标题。
       调用这个Intent 的时候,也必须指定具体的便笺(type为vnd.android.cursor.item/vnd.google.note)。不同的是,这里显示和编辑的只是便笺数据中的标题。

       有了这个功能,下面的Intent就会被解析到TitleEditor这个Activity:
{ action=com.google.android.notepad.action.EDIT_TITLE data=content://com.google.provider.NotePad/notes/{ID}}:显示并且允许用户编辑标识为ID的便笺的标题。


你可能感兴趣的:(android)