1. 程式人生 > >Intent 和 Intent Filter

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: or file: 並且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>