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

相關文章
相關標籤/搜索