Intent 和 Intent Filter
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接收到)
Intent Object
一個Intent物件是一個資訊包。它包含了要接收此Intent的元件需要的資訊(例如需要的動作和動作需要的資訊)和 android 系統需要的資訊(要處理此Intent的元件的類別和怎樣啟動它)
總的來說,Intent Object 主要包括以下資訊:
Component name
處理Intent 的元件名稱。此欄位是一個 ComponentName object---它是目標的元件的完整限定名(包名+類名) 例如: “com.android,.test.TestActivity” .
該欄位是可選的。如果設定了此欄位,那麼 Intent Object 將會被傳遞到這個元件名所對應的類的例項中。 如果沒有設定,Android 會用 Intent object 中的其它資訊去定位到一個合適的目標元件中。 (稱之為 : Intent 解析。。。這個稍後會講到)
設定Component name 可以通過 setComponent() , setClass() 或者 setClassName()進行設定。 可以通過 getComponent() 進行讀取
動作(Action)
一個字串,代表要執行的動作。 -- 或者,對於 broadcase intents 來說,表示正在發生,並且被報告的動作。Intent 類中 定義了許多動作常量。 如下:
Constent( 常量)
Target Component (目標元件)
Action (動作 )
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 strings 來啟用元件。自定義的action 應該包含包名作為字首: 例如"
com.example.project.SHOW_COLOR
".Action 很大程度上決定 Intent餘下部分的結構。 ---- 特別是:data 和 extras 兩個欄位。就像一個方法的方法名通常決定了方法的引數和返回值。 基於這個原因,應該給action 命名一個儘可能明確的名字。 可以通過 setAction() 設定action,通過 getAction() 進行獲取.
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() 方法。
Category
一個字串, 包含了處理該Intent的元件的種類資訊, 起著對action的補充說明作用.
一個Intent物件可以有任意多個 category。和action 一樣, 在Intent class 中也定義了幾個 category 常量。。 如下:
Constant
Meaning
CATEGORY_BROWSABLE
目標Activity可以使用瀏覽器顯示資料
CATEGORY_GADGET
The activity can be embedded inside of another activity that hosts gadgets.
該activity可以被包含在另外一個裝載小工具的activity中.
CATEGORY_HOME
The activity displays the home screen, the first screen the user sees when the device is turned on or when the HOME key is pressed.
CATEGORY_LAUNCHER
The activity can be the initial activity of a task and is listed in the top-level application launcher.
可以讓一個activity出現在launcher
CATEGORY_PREFERENCE
The target activity is a preference panel.
該activity是一個選項面板
addCategory() 新增一個 category
removeCategory() 刪除一個 category()
getCategorys() 獲取所有的category()
Extras
為鍵-值對形式的附加資訊. 例如ACTION_TIMEZONE_CHANGED的intent有一個"time-zone"附加資訊來指明新的時區, 而ACTION_HEADSET_PLUG有一個"state"附加資訊來指示耳機是被插入還是被拔出.
intent物件有一系列put...()和set...()方法來設定和獲取附加資訊. 這些方法和Bundle物件很像. 事實上附加資訊可以使用putExtras()和getExtras()作為Bundle來讀和寫.
Flags
有各種各樣的標誌,許多指示Android系統如何去啟動一個活動(例如,活動應該屬於那個任務)和啟動之後如何對待它(例如,它是否屬於最近的活動列表)。所有這些標誌都定義在Intent類中。
Intent Resolution
Intent 有兩種形式:
l 顯示意圖指定一個目標元件通過其name( Component name field), 由於元件名稱通常不會被其它應用程式的開發者知道。所以,顯示意圖通常用在應用程式內部訊息。----如:一個Activity 啟動一個從屬的service或者啟動另一個activity
l 隱式意圖不指定目標元件名稱(component name 是空的)隱式意圖通常用於去啟用其它應用程式的元件
Android 傳遞了一個顯示意圖給一個被指定的目標類的例項 。被傳遞的 intent object 只是定義了component name -- 它決定了將會有那個元件去處理這個intent。
針對隱式意圖需要不同的策略。在缺乏一個被指定的target的情況下,android系統必須找到最適合的元件去處理這個intent ---- 一個單一的activity 或者 service 去執行一個請求動作或者一組broadcase receiver 去響應廣播通知.
它通過將intent 物件中的內容 和 意圖過濾器(intent filters)進行比較。android系統根據intent filter開啟可以接收intent的元件. 如果一個元件沒有intent filter, 那麼它只能接受顯式intent. 如果有, 則能同時接受二者.。
Only three aspects of an Intent object are consulted when the object is tested against an intent filter:
當一個intent和intent過濾器進行比較時只會考慮以下三方面:
action
data (both URI and data type)
category
Intent filters
要告訴android系統哪個intent它們可以處理,activities,services,和 broadcast receivers 必須設定一個或者多個intent過濾器。每個過濾器描述了元件的一種能力,它過濾掉不想要的intent,留下想要的。顯示意圖則不用考慮這些。
一個過濾器中包含 一個Intent object 中的三個屬性 action,data,catrgory 。一個隱式意圖必須要通過這三項測試才能傳遞到 包含該過濾器的元件中。
測試1:Action test
<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,一個過濾器則可以列出多個action。這個列表也可以是空的, 一個過濾器必須包含一個 <action> element ,否則它將阻止所有的intents要通過這個測試,在intent被指定的action必須匹配在過濾器中所列的action的其中之一。如果一個intent物件或者過濾器沒有指定action。 結果如下 :
l 如果一個filter 沒有指定任何action ,那麼則沒有任何intent會被匹配。所以,所有的intent將不會通過此測試。
l 另一方面,如果一個intent物件沒有指定任何action,那麼將自動通過此測試—只要這個過濾器中有至少一個action
測試2:Category test
<intent-filter . . . >
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
. . .
</intent-filter>要通過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”,但沒必要)
測試3:Data test
類似於action, categories, data也是 intent filter 中的一個子節點, 可以設定多個 data節點,也可以一個不設定。
如下圖:
<intent-filter . . . >
<data android:mimeType="video/mpeg" android:scheme="http" . . . />
<data android:mimeType="audio/mpeg" android:scheme="http" . . . />
. . .
</intent-filter>每個< 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將被忽略。
<data>節點中的屬性都是可選的,但它們並非相互獨立。要使一個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,我們可以使用萬用字元進行部門匹配。
<data>
節點的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:
orfile:
並且filter不指定uri如果一個Intent 可以通過多個activity或者filter的filter,那麼使用者將會被詢問需要啟用哪個元件。 如果一個都沒有的話,將會丟擲異常。
Common cases
這個規則是針對 data test 中的規則d) ,它反映出元件可以從一個file或者content provider 獲取本地資料。因此,filters 可以是設定data type並且沒有必要明確的將 scheme 命名為content:
和 file:
。
下面的 <data>
元素,告訴android該元件可以從content provider中獲取image data 並顯示她。
<data android:mimeType="image/*" />
由於大部分可用的資料都是由content provider提供, 指定資料型別但不指定uri的filter是最常見的情況.
Another common configuration is filters with a scheme and a data type. For example, a <data> element like the following tells Android that the component can get video data from the network and display it:
設定了 scheme 和 data type是 另一個比較常見的配置是 。下面的 <data>
元素,告訴android該元件可以從網上獲取video並顯示
<data android:scheme="http" android:type="video/*" />
考慮當用戶在一個web page上點了一個連結後,瀏覽器應用程式做了什麼。 它首先會試圖去顯示該資料(當做一個html頁來處理)。如果它不能顯示此資料,它會使用一個設定 scheme 和 data type 的隱式意圖 去啟動一個能顯示此資料的activity。如果沒有找到接受者,它會呼叫下載管理器去下載該資料,然後將其放在content provider的控制之下,這樣很多activitys (那些之命名了datatype)可以處理該資料
大部分應用程式還有一種方式可以單獨啟動,不用去引用特別的資料。那些要啟動應用程式的activity 必須 設定 "android.intent.action.MAIN" 作為action。
如果還要顯示在程式啟動器上則必須設定 "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>