意圖和意圖過濾器Intents and Intent Filters

一個意圖Intent 對象是一堆信息。它包含接收這個意圖的組件感興趣的信息(例如將要採取的動作和操
作的數據)再加上Android 系統感興趣的信息(例如應該處理這個意圖的組件類別和如何啓動一個目標活
動的指令):
組件名稱Component name
應該處理這個意圖的組件名字. 這個字段是一個ComponentName 對象- 一個組合物:目標組
件的完全合格的類名(比如"com.example.project.app.FreneticActivity") 以
及應用程序描述文件中設置的組件所在包的名字(比如, "com.example.project"). 這個組
件名字的包部分和描述文件中設置的包名字不一定要匹配。
組件名字是可選的。如果被設置了,這個意圖對象將被傳遞到指定的類。如果沒有, Android 使
用另外的意圖對象中的信息去定位一個合適的目標- 請看本文稍後描述的意圖解析Intent
Resolution。
組件名字通過如下方法:setComponent(),setClass(), 或者setClassName()設置
並通過getComponent()讀取。
動作Action
一個將被執行的動作的字符串命名-或者, 對於廣播意圖而言, 是發生並被報告的動作。這個意圖
類定義了一些動作常量, 包含下面這些:
通過查看Intent 類描述可獲得一個通用動作的預定義常量列表。其他動作被定義在Android API
的其他地方。你也可以自定義動作字符串來激活應用程序中的組件。那些你所創建的動作字符串
應該以應用程序包名作爲前綴-例如:
"com.example.project.SHOW_COLOR".
動作很大程度上決定了意圖其他部分如何被組織-尤其是數據data 和附加字段extras-很像一個
方法名決定了一些參數和返回值. 因此, 一個好的想法是使用儘可能具體的動作名並和意圖的其
他字段緊密聯繫起來。換句話說,爲您的組件能處理的意圖對象定義一個整體的協議而不是定義
一個孤立的動作。
一個意圖對象裏的動作可以通過setAction()方法設置和通過getAction()方法讀取.
數據Data
常量目標組件Action
ACTION_CALL 活動開始一個電話呼叫
ACTION_EDIT 活動顯示數據以給用戶編輯
ACTION_MAIN 活動
開始任務的初始活動,沒有輸入數據也沒有輸
出返回
ACTION_SYNC 活動同步服務器與移動設備之間的數據
ACTION_BATTERY_LOW
廣播接收

電池低電量警告
ACTION_HEADSET_PLUG
廣播接收

耳機插拔
ACTION_SCREEN_ON
廣播接收

屏幕開啓
ACTION_TIMEZONE_CHANGED
廣播接收

時區變化
想要操作的數據統一資源標識符(URI)和那種數據的多用途互聯網郵件擴展(MIME). 不同的
動作伴隨着不同種類的數據規格。例如,如果動作是ACTION_EDIT,數據字段會包含可編輯文
檔的URI;如果動作是ACTION_CALL,數據字段會是一個電話號碼:含呼叫電
話號碼的URI;類似的,如果動作是ACTION_VIEW 而且數據字段是一個http:URI,
那麼接收到的活動將會是下載並顯示URI 所引用數據的請求。當匹配一個意圖到一個能處理數據
的組件時,除了它的URI 外,通常需要知道數據類型(它的MIME 類型)。
比如,一個能顯示圖片的組件不應該被要求去播放一個聲音文件。
在很多情況下,這個數據類型可以從URI 裏推斷出來-尤其是content:URIs, 這意味着數據被
存放在設備上而且由一個內容提供者控制着。(參閱separate discussion on content
providers). 但類型可以在意圖對象裏顯示的設置。setData()方法指定數據只能爲一個URI,
setType()指定它只能是一個MIME 類型, 而setDataAndType()指定它同時爲URI
和MIME 類型。URI 通過getData()讀取,類型則通過getType().
目錄Category
一個包含關於應該處理這個意圖的組件的附加信息的字符串。任意數目的類別描述可以被放到一
個意圖對象裏。和動作一樣,意圖類定義若干類別常量,包含如下這些:
查閱Intent 類描述可獲取類別的完整列表。
addCategory()方法在一個意圖對象中添加了一個目錄,removeCategory()刪除之前
添加的目錄,而getCategories()可以獲取當前對象的所有類別。
常量含義
CATEGORY_BROWSABLE
目標活動可以被瀏覽器安全的喚起來顯示被一個鏈接所引用的數
據-比如,一張圖片或一條e-mail 消息。
CATEGORY_GADGET 這個活動可以被嵌入到充當配件宿主的另外的活動裏面。
CATEGORY_HOME
這個活動將顯示桌面,也就是用戶開機後看到的第一個屏幕或者
按HOME 鍵時看到的屏幕。
CATEGORY_LAUNCHER
這個活動可以是一個任務的初始活動並被列在應用程序啓動器的
頂層。
CATEGORY_PREFERENCE 目標活動是一個選擇面板。
附加信息Extras
應該遞交給意圖處理組件的附加信息鍵-值對。就像一些動作伴隨着特定的數據URIs 類型,一些
動作則伴隨着特定的附加信息。比如,一個ACTION_TIMEZONE_CHANGED 意圖有一個“時
區”附加信息用來區別新的時區,而ACTION_HEADSET_PLUG 有一個“狀態”附加字段表明
耳機有沒有插着,以及一個“名字”附加信息來表示耳機的類型。如果你想要創建一個
SHOW_COLOR 動作,顏色的值將被設置在一個附加的鍵-值對中。
意圖對象有一系列的put...()方法來插入各種不同的附加數據和一個類似的用來讀取數據的
get...()方法系列。這些方法與Bundle 對象的方法相似。事實上,附加信息可以被當作一個
Bundle 通過使用putExtras()和getExtras()方法安裝和讀取。
標誌Flags
各種類型的標誌. 許多標誌用來指示Android 系統如何去加載一個活動(例如,哪個是這個活動
應該歸屬的任務)和啓動後如何對待它(比如,它是否屬於當前活動列表),所有這些列表都在
意圖類中定義了。
Android 系統以及這個平臺上的應用程序利用意圖對象來發送源於系統的廣播以及激活系統定義的組件。
要查閱如何組織一個意圖去激活一個系統組件,請諮詢引用中的意圖列表list of intents。
意圖解析Intent Resolution
意圖可以被分成兩組:
• 顯式意圖通過名字指明目標組件(這個組件名字字段component name field, 前面提到過,
有一個數值集)。既然組件名稱通常不爲其他應用程序的開發者所瞭解,顯式意圖典型的被用作
應用程序的內部消息-例如一個活動啓動一個附屬服務或姊妹活動。
• 隱式意圖不命名目標組件(組件名稱字段爲空)。隱式意圖經常用來激活其他應用程序的組件。
Android 遞交一個顯式的意圖給一個指定目標類的實例。意圖對象中的組件名稱唯一的確定哪個組件應該
獲取這個意圖。隱式意圖需要一個不同的策略。在沒有指定目標的情況下,Android 系統必須找到最合適
的組件來處理這個意圖-單個活動或者服務來執行這個請求動作或者一系列的廣播接收器來應對廣播通告。
這是通過比較意圖對象的內容和意圖過濾器,有可能接收意圖的組件相關結構。過濾器公佈一個組件具備
的能力以及限定它能處理的意圖。他們使組件接收該公佈類型的隱式意圖成爲可能。如果一個組件沒有任
何的意圖過濾器,那它只能接收顯式意圖。一個帶過濾器的組件可以同時接收顯式和隱式意圖。
當一個意圖對象被一個意圖過濾器測試時,只有三個方面會被參考到:
動作
數據(URI 以及數據類型)
類別
附加信息和標誌並不參與解析哪個組件接收一個意圖。
意圖過濾器Intent filters
爲了通知系統它們可以處理哪些意圖,活動、服務和廣播接收器可以有一個或多個意圖過濾器。每個過濾
器描述組件的一個能力,一系列組件想要接收的意圖。它實際上按照一個期望的類型來進行意圖濾入,同
時濾出不想要的意圖-但是隻有不想要的隱式意圖會被濾出(那些沒有命名目標的對象類)。一個顯式意圖
總能夠被遞交給它的目標,而無論它包含什麼。這種情況下過濾器不起作用。但是一個顯式意圖僅當它能
通過組件的一個過濾器時纔可以被遞交到這個組件。
組件爲它能做的每項工作,每個呈現給用戶的不同方面分有不同的過濾器。比如,範例記事本應用程序中
的主要活動有三個過濾器-一個是空白板,另一個是用戶可以查看、編輯、或選擇的一個指定的記事目錄,
第三是在沒有初始目錄說明的情況下查找一個特定的記錄。一個意圖過濾器是IntentFilter 類的一個實例。
但是,由於Android 系統在啓動一個組件前必須知道這個組件的能力,意圖過濾器通常不會用Java 代碼
來設置,而是在應用程序清單文件(AndroidManifest.xml)中設置<intent-filter>元素。(有一個例外,
通過調用Context.registerReceiver() 來註冊的廣播接收器的過濾器;它們是作爲意圖過濾器
對象而被直接創建的。
過濾器與安全Filters and security
不能信賴一個意圖過濾器的安全性。當它打開一個組件來接收某些特定類型的隱式意圖,它並不能阻止以
這個組件爲目標的顯式意圖。即使過濾器對組件要處理的意圖限制某些動作和數據源,總有人能把一個顯
式意圖和一個不同的動作及數據源組合在一起,然後命名該組件爲目標。
一個過濾器和意圖對象有同樣的動作、數據以及類別字段。一個隱式意圖在過濾器的所有三個方面都被測
試。爲了遞交到擁有這個過濾器的組件,它必須通過所有這三項測試。即便只有一個不通過,Android 系
統都不會把它遞交給這個組件-至少以那個過濾器的標準而言。不過,由於一個組件可以包含多個意圖過濾
器,一個不能通過其中一個組件過濾器的意圖可能在另外的過濾器上獲得通過。
三個測試詳細描述如下:
動作測試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>
如同例子所示,一個意圖對象只對單個動作命名,而一個過濾器可能列舉多個。列表不能爲空;
一個過濾器必須包含至少一個動作元素,否則它將阻塞所有的意圖。
爲了通過這個測試,在意圖對象中指定的動作必須匹配過濾器中所列舉的動作之一。如果意圖對
象或過濾器不指定一個動作,結果將如下:
• 如果這個過濾器沒有列出任何動作,那意圖就沒有什麼可匹配的,因此所有的意圖都會
測試失敗。沒有意圖能夠通過這個過濾器。
• 另一方面,一個未指定動作的意圖對象自動通過這個測試-只要過濾器包含至少一個動
作。
類別測試Category test
一個意圖過濾器<intent-filter>元素也列舉了類別作爲子元素。比如:
<intent-filter . . . >
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
. . .
</intent-filter>
注意前面描述的動作和類別常量沒有在清單文件中使用。相反使用了完整的字符串。比如,對應
於前述CATEGORY_BROWSABLE 常量,上面的例子裏使用了
"android.intent.category.BROWSABLE"字符串。類似的,字符串
"android.intent.action.EDIT" 對應於ACTION_EDIT 常量。
對一個通過類別測試的意圖,每個意圖對象中的類別必須匹配一個過濾器中的類別。這個過濾器
可以列舉另外的類別,但它不能遺漏任何在這個意圖中的類別。
因此,原則上一個沒有類別的意圖對象應該總能夠通過測試,而不管過濾器裏有什麼。絕大部分
情況下這個是對的。但有一個例外,Android 把所有傳給startActivity()的隱式意圖當作他們包
含至少一個類別:"android.intent.category.DEFAULT" (CATEGORY_DEFAULT
常量)。因此,想要接收隱式意圖的活動必須在它們的意圖過濾器中包含
"android.intent.category.DEFAULT"。( 帶"android.intent.action.MAIN"
和"android.intent.category.LAUNCHER"設置的過濾器是例外)。它們標記那些啓動
新任務和呈現在啓動屏幕的活動。它們可以在類別列表中包含
"android.intent.category.DEFAULT",但不是必要的。) 可查閱後面的使用意圖匹配
(Using intent matching)以獲得更多關於過濾器的信息。
數據測試Data test
就像動作和類別,一個意圖過濾器的數據規格被包含在一個子元素中。而且這個子元素可以出現
多次或一次都不出現。例如:
<intent-filter . . . >
<data android:type="video/mpeg" android:scheme="http" . . . />
<data android:type="audio/mpeg" android:scheme="http" . . . />
. . .
</intent-filter>
每個數據<data>元素可以指定一個URI 和一個數據類型(MIME 媒體類型)。有一些單獨的屬
性-模式,主機,端口和路徑-URI 的每個部分:
scheme://host:port/path
比如,在下面的URI 裏面,
content://com.example.project:200/folder/subfolder/etc
模式是"內容",主機是"com.example.project",端口是"200",路經是
"folder/subfolder/etc"。主機和端口一起組成URI 鑑權(authority);如果未指
定主機,端口會被忽略。
這些屬性都是可選的,但彼此有依賴關係:一個授權要有意義,必須指定一個模式。一個路經要
有意義,必須同時指定模式和鑑權。
當一個意圖對象中的URI 被用來和一個過濾器中的URI 規格比較時,它實際上比較的是上面提
到的URI 的各個部分。比如,如果過濾器僅指定了一個模式,所有那個模式的URIs 和這個過濾
器相匹配;如果過濾器指定了一個模式、鑑權但沒有路經,所有相同模式和鑑權的URIs 可以匹
配上,而不管它們的路經;如果過濾器指定了一個模式、鑑權和路經,只有相同模式、鑑權和路
經的URIs 可以匹配上。當然,一個過濾器中的路徑規格可以包含通配符,這樣只需要部分匹配
即可。
數據<data>元素的類型屬性指定了數據的MIME 類型。這在過濾器裏比在URI 裏更爲常見。意
圖對象和過濾器都可以使用一個"*"通配符指定子類型字段-比如,"text/*"或者"audio/*"-
指示任何匹配的子類型。
數據測試同時比較意圖對象和過濾器中指定的URI 和數據類型。規則如下:
a. 一個既不包含URI 也不包含數據類型的意圖對象僅在過濾器也同樣沒有指定任何URIs
和數據類型的情況下才能通過測試。
b. 一個包含URI 但沒有數據類型的意圖對象僅在它的URI 和一個同樣沒有指定數據類型
的過濾器裏的URI 匹配時才能通過測試。這通常發生在類似於mailto:和tel:這樣的
URIs 上:它們並不引用實際數據。
c. 一個包含數據類型但不包含URI 的意圖對象僅在這個過濾器列舉了同樣的數據類型而
且也沒有指定一個URI 的情況下才能通過測試。
d. 一個同時包含URI 和數據類型(或者可從URI 推斷出數據類型)的意圖對象可以通過
測試,如果它的類型和過濾器中列舉的類型相匹配的話。如果它的URI 和這個過濾器中
的一個URI 相匹配或者它有一個內容content:或者文件file: URI 而且這個過濾器沒有
指定一個URI,那麼它也能通過測試。換句話說,一個組件被假定爲支持content:和file:
數據如果它的過濾器僅列舉了一個數據類型。
如果一個意圖可以通過不止一個活動或服務的過濾器,用戶可能會被詢問要激活那個組件。如果沒有發現
目標對象將會出現異常。
通常情況Common cases
上面描述的數據測試的最後一個規則(d),表達了這樣一個期望即組件能夠從文件或內容提供者中獲取本
地數據。因此,它們的過濾器可以只列舉一個數據類型而不需要顯式的命名content:和file:模式。這是
一個典型情況。比如,一個如下的數據<data>元素,告訴Android 這個組件能從內容提供者獲取圖片數
據並顯示:
<data android:type="image/*" />
既然大多數可用數據是通過內容提供者來分發,那麼過濾器最通常的配置就是指定一個數據類型而不指定
URI。另外一個通用的配置是帶有一個模式和數據類型的過濾器。比如,一個如下的數據<data>元素告
訴Android 可以從網絡獲取視頻數據並顯示:
<data android:scheme="http" android:type="video/*" />
比如,想一下,當用戶點擊網頁上的一個鏈接時瀏覽器做了什麼。它首先試圖去顯示這個數據(如果這個
鏈接指向一個HTML 頁面)。如果它不能顯示這個數據,它會把一個顯式意圖和一個模式、數據類型組成
整體然後嘗試啓動一個可以處理這個工作的活動。如果沒有接受者,它將要求下載管理器來下載數據。這
讓它處於內容提供者的控制下,以便一個潛在的更大的活動池可以做出反應。
大多數應用
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章