Android Intent and Intent Filter

Intents and Intent Filters

 

三种应用程序基本组件——activity, service和broadcast receiver——是使用称为intent的消息来激活的。Intent消息传递是一种组件间运行时绑定的机制. intent是Intent对象, 它包含了须要作的操做的描述, 或者, 对于广播来讲, 包含了正在通知的消息内容. 对于向这三种组件发送intent有不一样的机制: html

  • 使用Context.startActivity() 或 Activity.startActivityForResult(), 传入一个intent来启动一个activity. 使用 Activity.setResult(), 传入一个intent来从activity中返回结果.
  • 将intent对象传给Context.startService()来启动一个service或者传消息给一个运行的service. 将intent对象传给 Context.bindService()来绑定一个service.
  • 将intent对象传给 Context.sendBroadcast()Context.sendOrderedBroadcast(),或者Context.sendStickyBroadcast()等广播方法,则它们被传给 broadcast receiver.

 

在上述三种状况下, android系统会本身找到合适的activity, service, 或者  broadcast receivers来响应intent. 三者的intent相互独立互不干扰. android

 

Intent Objects Intent对象

 

一个intent对象包含了接受该intent的组件的信息(例如须要的动做和该动做须要的数据)和android系统所须要的信息(例如该组件的类别,以及如何启动它). 具体的说: windows

组件名称
为一个 ComponentName 对象. 它是目标组件的完整名(例如" com.example.project.app.FreneticActivity")和应用程序manifest文件设定的包名(例如" com.example.project")的组合.前者的包名部分和后者不必定同样.

 

组件名称是可选的. 若是设定了的话, Intent对象会被传给指定的类的一个实例. 若是不设定, 则android使用其它信息来定位合适的目标. 浏览器

 

组件名称是使用setComponent()setClass(),或  setClassName()来设定, 使用 getComponent()来获取. 安全

Action
一个字符串, 为请求的动做命名, 或者, 对于broadcast intent, 发生的而且正在被报告的动做. 例如:
常量 目标组件
动做
ACTION_CALL activity 发起一个电话呼叫.
ACTION_EDIT activity 显示数据给用户来编辑.
ACTION_MAIN activity 将该activity做为一个task的第一个activity启动,不传入参数也不指望返回值.
ACTION_SYNC activity 将设备上的数据和一个服务器同步.
ACTION_BATTERY_LOW broadcast receiver 发出电量不足的警告.
ACTION_HEADSET_PLUG broadcast receiver 一个耳机正被插入或者拔出.
ACTION_SCREEN_ON broadcast receiver 屏幕被点亮.
ACTION_TIMEZONE_CHANGED broadcast receiver 时区设置改变.

 

你也能够定义本身的action字符串用来启动你的应用程序. 自定义的action应该包含应用程序的包名.例如"com.example.project.SHOW_COLOR". 服务器

 

action很大程度上决定了intent的另外部分的结构, 就像一个方法名决定了它接受的参数和返回值同样. 所以, 最好给action一个最能反映其做用的名字. 网络

 

一个intent对象中的action是使用getAction()和setAction()来读写的. app

Data
须要操做的数据的URI和它的MIME(多用途互联网邮件扩展,Multipurpose Internet Mail Extensions)类型. 例如, 若是action为ACTION_EDIT, 那么Data将包含待编辑的数据URI. 若是action为ACTION_CALL, Data将为tel:电话号码的URI. 若是action为ACTION_VIEW, 则Data为http:网络地址的URI.

 

当将一个intent和一个组件相匹配时, 除了URI外数据类型也很重要. 例如, 一个显示图片的程序不该该用来处理声音文件. 编辑器

 

数据类型经常能够从URI推断, 特别是content:URI, 它表示该数据属于一个content provider. 但数据类型也能够被intent对象显示声明. setData()方法设置URI, 而setType()方法指定MIME类型, setDataAndType()设置数据URI和MIME类型. 它们可使用getData()和getType()来读取. ide

Category
一个字符串,包含了关于处理该intent的组件的种类的信息. 一个intent对象能够有任意个category. intent类定义了许多category常数, 例如:
常量 含义
CATEGORY_BROWSABLE 目标activity可使用浏览器来显示-例如图片或电子邮件消息.
CATEGORY_GADGET 该activity能够被包含在另一个装载小工具的activity中.
CATEGORY_HOME 该activity显示主屏幕,也就是用户按下Home键看到的界面.
CATEGORY_LAUNCHER 该activity能够做为一个任务的第一个activity,而且列在应用程序启动器中.
CATEGORY_PREFERENCE 该activity是一个选项面板.

 

 

addCategory()方法为一个intent对象增长一个category, removeCategory删除一个category, getCategories()获取intent全部的category.

Extras
为键-值对形式的附加信息. 例如 ACTION_TIMEZONE_CHANGED的intent有一个"time-zone"附加信息来指明新的时区, 而 ACTION_HEADSET_PLUG有一个"state"附加信息来指示耳机是被插入仍是被拔出.

 

intent对象有一系列put...()和set...()方法来设定和获取附加信息. 这些方法和Bundle对象很像. 事实上附加信息可使用putExtras()和getExtras()做为Bundle来读和写.

Flags

各类标志. 不少标志指示android系统如何启动一个activity(例如该activity属于哪一个任务)和启动后如何处理它(例如, 它是否属于最近activity列表中).

 

android系统和应用程序使用intent对象来送出系统广播和激活系统定义的组件.

Intent Resolution Intent解析

 

intent有两种:

  • 显式intent使用名字来指定目标组件. 因为组件名称通常不会被其它开发者所熟知, 这种intent通常用于应用程序内部消息-- 例如一个activity启动一个附属的service或者另外一个activity.
  • 隐式intent不指定目标的名称. 通常用于启动其它应用程序的组件.

Android将显式intent发送给指定的类. intent对象中名字惟一决定接受intent的对象.

 

对于隐式intent, android系统必须找到最合适的组件来处理它. 它比较intent的内容和intent filter.  intent filter是组件的一个相关结构, 表示其接受intent的能力. android系统根据intent filter打开能够接受intent的组件. 若是一个组件没有intent filter, 那么它只能接受显式intent. 若是有, 则能同时接受两者.

当一个intent和intent filter比较时, 只考虑三个属性: action, data, category.

extra和flag在intent解析中没有用.

Intent filters

 

activity, service和broadcast receiver能够有多个intent filter来告知系统它们能接受什么样的隐式intent. intent filter的名字很形象: 它过滤掉不想接受的intent, 留下想接受的intent. 显式intent无视intent filter.

一个组件对能作的每件事有单独的filter. 例如, 记事本程序的NoteEditor activity有两个filter -- 一个启动并显示一个特定的记录给用户查看或编辑, 另外一个启动一个空的记录给用户编辑.

 

 

一个intent filter是IntentFilter类的实例, 可是它通常不出如今代码中,而是出如今android Manifest文件中, 以<intent-filter>的形式. (有一个例外是broadcast receiver的intent filter是使用 Context.registerReceiver()来动态设定的, 其intent filter也是在代码中建立的.)

一个filter有action, data, category等字段. 一个隐式intent为了能被某个intent filter接受, 必须经过3个测试. 一个intent为了被某个组件接受, 则必须经过它全部的intent filter中的一个.

 

Action 测试
<intent-filter . . . >
    
<action android:name="com.example.project.SHOW_CURRENT" />
    
<action android:name="com.example.project.SHOW_RECENT" />
    
<action android:name="com.example.project.SHOW_PENDING" />
    . . .
</intent-filter>

 

 

一个intent对象只能指定一个action, 而一个intent filter能够指定多个action. action列表不能为空, 不然它将组织全部的intent.

 

一个intent对象的action必须和intent filter中的某一个action匹配, 才能经过.

若是intent filter的action列表为空, 则不经过.

若是intent对象不指定action, 而且intent filter的action列表不为空, 则经过.

Category 测试

<intent-filter . . . >

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

 

 

注意前面说到的对于action和category的常数是在代码中用的,而不是manifest文件中用的. 例如,CATEGORY_BROWSABLE常数对应xml中的表示为"android.intent.category.BROWSABLE".

一个intent要经过category测试, 那么该intent对象中的每一个category都必须和filter中的某一个匹配.

 

理论上来讲, 一个intent对象若是没有指定category的话, 它应该能经过任意的category 测试. 有一个例外: android把全部的传给startActivity()的隐式intent看作至少有一个category: "android.intent.category.DEFAULT". 所以, 想要接受隐式intent的activity必须在intent filter中加入"android.intent.category.DEFAULT". ("android.intent.action.MAIN" 和"android.intent.category.LAUNCHER"的intent filter例外. 它们不须要"android.intent.category.DEFAULT".)


Data test
<intent-filter . . . >
    
<data android:mimeType="video/mpeg" android:scheme="http" . . . />
    
<data android:mimeType="audio/mpeg" android:scheme="http" . . . />
    . . .
</intent-filter>

 

 

每一个<data>元素指定了一个URI和一个数据类型. URI每一个部分为不一样的属性 -- scheme, host, port, path:

scheme://host:port/path

例如, 在以下的URI中:

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

 

scheme为"content", host为"com.example.project", port为"200", path为"folder/subfolder/etc". host和port一块儿组成了URI authority. 若是host未指定,则port被忽略.

 

这些属性都是可选的,但它们并不是相互独立: 要使一个authority有意义,必须指定一个scheme. 要使一个path有意义, 必须指定一个scheme和一个authority.

 

当intent对象中的URI和intent filter中相比较时, 它只和filter中定义了的部分比较. 例如, 若是filter中之定义了scheme,那么全部包含该scheme的URI的intent对象都经过测试.对于path来讲,可使用通配符来进行部分匹配.

<data>元素的type属性指定了数据类型. 它在filter中比在URI中更常见. intent对象和filter均可以使用"*"通配符做为子类型. 例如"text/*" "audio/*"表示全部的子类型都匹配.

 

data测试的规则以下:

  1. 一个不含uri也不含数据类型的intent对象只经过二者都不包含的filter.
  2. 一个含uri但不含数据类型的intent对象(而且不能从uri推断数据类型的)只能经过这样的filter: uri匹配, 而且不指定类型. 这种状况限于相似mailto:和tel:这样的不指定实际数据的uri.
  3. 一个只包含数据类型但不包含URI的intent只经过这样的filter: 该filter只列出相同的数据类型, 而且不指定uri.
  4. 一个既包含uri又包含数据类型的intent对象只经过这样的filter: intent对象的数据类型和filter中的一个类型匹配, intent对象的uri要么和filter的uri匹配, 要么intent对象的uri为content:或者file:, 而且filter不指定uri.

 

若是一个intent能够经过多于一个activity或者service的filter, 那么用户可能会被询问须要启动哪个. 若是一个都没有的话, 那么会抛出异常.

Common cases 常见状况

 

上述的最后一个规则(d)说明了组件一般能够从文件和content provider中获取数据. 所以, 它们的filter能够只列出数据类型不列scheme. 这是个特殊状况. 下列<data>元素告诉android该组件能够从一个content provider取得图像数据并显示之:

<data android:mimeType="image/*" />

 

因为大部分可用的数据由content provider提供, 指定数据类型但不指定uri的filter是最多见的状况.

 

另一个常见的配置是filter具备一个scheme和一个数据类型. 例如, 下列<data>元素告诉android该component能够从网络获取图像数据并显示之:

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

 

考虑用户点击一个网页时浏览器的动做. 它首先试图显示这个数据(当作一个html页来处理). 若是没法显示, 则建立一个隐式intent, 并启动一个能够处理它的activity. 若是没有这样的activity, 那么它请求下载管理器来下载该数据. 而后它将数据置于一个content provider的控制之下, 这样有不少activity(拥有只有数据类型的filter)能够处理这些数据.

 

大部分应用程序还有一种方法来单独启动, 不须要引用任何特定的数据. 这些能启动应用程序的activity具备action为"android.intent.action.MAIN" 的filter. 若是它们须要在应用程序启动器中显示, 它们必须指定"android.intent.category.LAUNCHER" 的category.

<intent-filter . . . >
    
<action android:name="code android.intent.action.MAIN" />
    
<category android:name="code android.intent.category.LAUNCHER" />
</intent-filter>

Using intent matching 使用intent匹配

 

intent和intent filter相匹配, 不只为了寻找并启动一个目标组件, 也是为了寻找设备上组件的信息. 例如, android系统启动了应用程序启动器, 该程序位于屏幕的顶层, 显示了用户能够启动的程序, 这是经过查找设备上全部的action为"android.intent.action.MAIN" ,category为"android.intent.category.LAUNCHER"的intent filter所在的activity实现的. 而后它显示了这些activity的图标和标题. 相似的, 它经过寻找 "android.intent.category.HOME"的filter来定位主屏幕程序.

 

应用程序能够用相同的方式来使用intent匹配. PackageManager 有一组query...()方法来寻找接受某个特定intent的全部组件, 还有一系列resolve...()方法来决定响应一个intent的最佳组件. 例如, queryIntentActivities()返回一个activity列表, 这些activity能够执行传入的intent. 相似的还有queryIntentServices()和queryIntentBroadcastReceivers().

Note Pad Example 例子:记事本

 

记事本示例程序让用户能够浏览一个笔记列表, 查看, 编辑, 删除和增长笔记. 这一节关注该程序定义的intent filter.

 

 

在其manifest文件中, 记事本程序定义了三个activity, 每一个有至少一个intent filter. 它还定义了一个content provider来管理笔记数据. manifest 文件以下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          
package="com.example.android.notepad">
    
<application android:icon="@drawable/app_notes"
                 
android:label="@string/app_name" >

        
<provider android:name="NotePadProvider"
                  
android:authorities="com.google.provider.NotePad" />

        
<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>
            
<intent-filter>
                
<action android:name="android.intent.action.GET_CONTENT" />
                
<category android:name="android.intent.category.DEFAULT" />
                
<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
            
</intent-filter>
        
</activity>
        
        
<activity android:name="NoteEditor"
                  
android:theme="@android:style/Theme.Light"
                  
android:label="@string/title_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.notepad.action.EDIT_NOTE" />
                
<category android:name="android.intent.category.DEFAULT" />
                
<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
            
</intent-filter>
            
<intent-filter>
                
<action android:name="android.intent.action.INSERT" />
                
<category android:name="android.intent.category.DEFAULT" />
                
<data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
            
</intent-filter>
        
</activity>
        
        
<activity android:name="TitleEditor"
                  
android:label="@string/title_edit_title"
                  
android:theme="@android:style/Theme.Dialog">
            
<intent-filter android:label="@string/resolve_title">
                
<action android:name="com.android.notepad.action.EDIT_TITLE" />
                
<category android:name="android.intent.category.DEFAULT" />
                
<category android:name="android.intent.category.ALTERNATIVE" />
                
<category android:name="android.intent.category.SELECTED_ALTERNATIVE"/>
                
<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
            
</intent-filter>
        
</activity>
        
    
</application>
</manifest>

 

第一个activity, NoteList, 和其它activity不一样, 由于它操做一个笔记的目录(笔记列表), 而不是一个单独的笔记. 它通常做为该程序的初始界面. 它能够作如下三件事:

  1. <intent-filter>
        
    <action android:name="android.intent.action.MAIN" />
        
    <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    该filter声明了记事本应用程序的主入口. 标准的MAIN action是一个不须要任何其它信息(例如数据等)的程序入口, LAUNCHER category表示该入口应该在应用程序启动器中列出.

  2. <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>

    该filter声明了改activity能够对一个笔记目录作的事情. 它容许用户查看或编辑该目录(使用VIEW和EDIT action), 或者选取特定的笔记(使用PICK action).

    <data>元素的mimeType指定了这些action能够操做的数据类型. 它代表该activity能够从一个持有记事本数据的content provider(vnd.google.note)取得一个或多个数据项的Cursor(vnd.android.cursor.dir).

    注意该filter提供了一个DEFAULT category. 这是由于 Context.startActivity() 和Activity.startActivityForResult()方法将全部的intent都做为做为包含了DEFAULT category来处理, 只有两个例外:

    • 显式指明目标activity名称的intent.
    • 包含MAIN action 和LAUNCHER category的intent.

    所以, 除了MAIN和LAUNCHER的filter以外, DEFAULT category是必须的.

  3. <intent-filter>
        
    <action android:name="android.intent.action.GET_CONTENT" />
        
    <category android:name="android.intent.category.DEFAULT" />
        
    <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    </intent-filter>

    这个filter描述了该activity可以在不须要知道目录的状况下返回用户选择的一个笔记的能力. GET_CONTENT action和PICK action相相似. 在这二者中, activity都返回用户选择的笔记的URI. (返回给调用startActivityForResult()来启动NoteList activity的activity.) 在这里, 调用者指定了用户选择的数据类型而不是数据的目录.

    这个数据类型, vnd.android.cursor.item/vnd.google.note, 表示了该activity能够返回的数据类型 -- 一个笔记的URI. 从返回的URI, 调用者能够从持有笔记数据的content provider(vnd.google.note)获得一个项目(vnd.android.cursor.item)的Cursor.

    也就是说, 对于PICK来讲, 数据类型表示activity能够给用户显式的数据类型.对于GET_CONTENT filter, 它表示activity能够返回给调用者的数据类型.

 

下列intent能够被NoteList activity接受:

action:  android.intent.action.MAIN
不指定任何数据直接启动activity.

action:  android.intent.action.MAIN
category:  android.intent.category.LAUNCHER
不指定任何数据直接启动activity. 这是程序启动器使用的intent. 全部使用该组合的filter的activity被加到启动器中.

action:  android.intent.action.VIEW
data:  content://com.google.provider.NotePad/notes
要求activity显示一个笔记列表,这个列表位于 content://com.google.provider.NotePad/notes. 用户能够浏览这个列表并获取列表项的信息.

action:  android.intent.action.PICK
data:  content://com.google.provider.NotePad/notes
请求activity显示 content://com.google.provider.NotePad/notes下的笔记列表. 用户能够选取一个笔记, activity将返回笔记的URI给启动NoteList的activity.

action:  android.intent.action.GET_CONTENT
data type:  vnd.android.cursor.item/vnd.google.note
请求activity提供记事本数据的一项.

 

 

第二个activity, NoteEditor, 为用户显示一个笔记并容许他们编辑它. 它能够作如下两件事:

  1. <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.notepad.action.EDIT_NOTE" />
        
    <category android:name="android.intent.category.DEFAULT" />
        
    <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    </intent-filter>

    这个activity的主要目的是使用户编辑一个笔记--VIEW或者EDIT一个笔记. (在category中,EDIT_NOTE是EDIT的同义词.) intent包含匹配MIME类型vnd.android.cursor.item/vnd.google.note的URI--也就是某一个特定的笔记的URI. 它通常来讲是NoteList activity中的PICK或者GET_CONTENT action返回的.

    像之前同样,该filter列出了DEFAULT category.

  2. <intent-filter>
        
    <action android:name="android.intent.action.INSERT" />
        
    <category android:name="android.intent.category.DEFAULT" />
        
    <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
    </intent-filter>

    该activity的第二个目的是使用户可以建立一个新的笔记, 并插入到已存在的笔记目录中. 该intent包含了匹配vnd.android.cursor.dir/vnd.google.note的URI, 也就是笔

 

有了这些能力, NoteEditor就能够接受如下intent:

action:  android.intent.action.VIEW
data:  content://com.google.provider.NotePad/notes/ID
要求activity显示给定ID的笔记.

action:  android.intent.action.EDIT
data:  content://com.google.provider.NotePad/notes/ID
要求activity显示指定ID的笔记,而后让用户来编辑它. 若是用户保存了更改,则activity更新该content provider的数据.

action:  android.intent.action.INSERT
data:  content://com.google.provider.NotePad/notes
要求activity建立一个新的空笔记在 content://com.google.provider.NotePad/notes, 并容许用户编辑它, 若是用户保存了更改,则该URI被返回给调用者.

最后一个activity, TitleEditor, 容许用户编辑笔记的标题. 这能够经过直接调用activity(在intent中设置组件名称)的方式来实现. 可是这里咱们用这个机会来展现如何在已有数据上进行另外的操做(相似于windows中的打开方式->程序列表 -- 译者注):

<intent-filter android:label="@string/resolve_title">
    
<action android:name="com.android.notepad.action.EDIT_TITLE" />
    
<category android:name="android.intent.category.DEFAULT" />
    
<category android:name="android.intent.category.ALTERNATIVE" />
    
<category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
    
<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
它必须在一个特定的笔记上调用(data type  vnd.android.cursor.item/vnd.google.note), 就像以前的 VIEWEDIT action同样. 然而, 这里activity显示笔记数据中包含的标题, 而不是内容.

 

除了支持DEFAULT category以外,title编辑器还支持了另外两个category: ALTERNATIVESELECTED_ALTERNATIVE. 这些category标志着activity能够在选项菜单中呈现给用户(就像LAUNCHER category表示activity能够在程序启动器中同样). 注意filter还提供了一个显示标签(android:label="@string/resolve_title")来更好的控制用户在选项菜单中看到的内容.

 

有了这些能力, 如下的intent就能够被TitleEditor接受:

action:  com.android.notepad.action.EDIT_TITLE
data:  content://com.google.provider.NotePad/notes/ID
要求activity显示给定笔记ID的标题, 并容许用户编辑该标题.

http://blog.csdn.net/lmhit/article/details/5576250

相关文章
相关标签/搜索