意圖和意圖過濾器Intents and Intent Filters
一個應(yīng)用程序的三個核心組件-活動,服務(wù)和廣播接收器是通過消息即意圖(Intents)來激活的。Intent消息傳送是相同或不同應(yīng)用中組件運(yùn)行時晚綁定的一種機(jī)制。意圖本身,一個意圖對象,是一個包含被執(zhí)行操作抽象描述的被動的數(shù)據(jù)結(jié)構(gòu)-或者,對于廣播而言,是某件已經(jīng)發(fā)生并被聲明的事情的描述。存在不同的機(jī)制來傳送意圖到每種組件中:
在每個例子里,Android系統(tǒng)找到合適的活動,服務(wù),或者一組廣播接收者來回應(yīng)這個意圖,必要時實例化它們。這些消息傳送系統(tǒng)沒有重疊:廣播意圖僅被傳遞給廣播接收者,永遠(yuǎn)不會給活動或者服務(wù)。一個傳送給startActivity()的意圖是只會被傳遞給一個活動,永遠(yuǎn)不會給一個服務(wù)或廣播接收者,如此類推。 這篇文檔以意圖對象的描述開始,然后描述Android映射意圖到組件的規(guī)則-如何解決哪個組件應(yīng)該接收一個意圖消息。對于沒有顯式命名一個目標(biāo)組件的意圖,這個過程包括對照與潛在目標(biāo)相關(guān)聯(lián)的意圖過濾器來測試這個意圖對象。
意圖對象Intent Objects 一個意圖Intent對象是一堆信息。它包含接收這個意圖的組件感興趣的信息(例如將要采取的動作和操作的數(shù)據(jù))再加上Android系統(tǒng)感興趣的信息(例如應(yīng)該處理這個意圖的組件類別和如何啟動一個目標(biāo)活動的指令): 應(yīng)該處理這個意圖的組件名字. 這個字段是一個ComponentName對象- 一個組合物:目標(biāo)組件的完全合格的類名 (比如"com.example.project.app.FreneticActivity") 以及應(yīng)用程序描述文件中設(shè)置的組件所在包的名字(比如, "com.example.project"). 這個組件名字的包部分和描述文件中設(shè)置的包名字不一定要匹配。 組件名字是可選的。如果被設(shè)置了,這個意圖對象將被傳遞到指定的類。如果沒有, Android使用另外的意圖對象中的信息去定位一個合適的目標(biāo)- 請看本文稍后描述的意圖解析Intent Resolution。 組件名字通過如下方法:setComponent(),setClass(), 或者setClassName()設(shè)置并通過getComponent()讀取。
動作Action 一個將被執(zhí)行的動作的字符串命名-或者, 對于廣播意圖而言, 是發(fā)生并被報告的動作。這個意圖類定義了一些動作常量, 包含下面這些:
通過查看Intent類描述可獲得一個通用動作的預(yù)定義常量列表。其他動作被定義在Android API的其他地方。你也可以自定義動作字符串來激活應(yīng)用程序中的組件。那些你所創(chuàng)建的動作字符串應(yīng)該以應(yīng)用程序包名作為前綴-例如: "com.example.project.SHOW_COLOR". 動作很大程度上決定了意圖其他部分如何被組織-尤其是數(shù)據(jù)data和附加字段extras-很像一個方法名決定了一些參數(shù)和返回值. 因此, 一個好的想法是使用盡可能具體的動作名并和意圖的其他字段緊密聯(lián)系起來。換句話說,為您的組件能處理的意圖對象定義一個整體的協(xié)議而不是定義一個孤立的動作。 一個意圖對象里的動作可以通過setAction()方法設(shè)置和通過getAction()方法讀取.
想要操作的數(shù)據(jù)統(tǒng)一資源標(biāo)識符(URI)和那種數(shù)據(jù)的多用途互聯(lián)網(wǎng)郵件擴(kuò)展(MIME). 不同的動作伴隨著不同種類的數(shù)據(jù)規(guī)格。例如,如果動作是ACTION_EDIT,數(shù)據(jù)字段會包含可編輯文檔的URI;如果動作是ACTION_CALL,數(shù)據(jù)字段會是一個電話號碼:含呼叫電話號碼的URI;類似的,如果動作是ACTION_VIEW而且數(shù)據(jù)字段是一個http:URI, 那么接收到的活動將會是下載并顯示URI所引用數(shù)據(jù)的請求。當(dāng)匹配一個意圖到一個能處理數(shù)據(jù)的組件時,除了它的URI外,通常需要知道數(shù)據(jù)類型(它的MIME類型)。 比如,一個能顯示圖片的組件不應(yīng)該被要求去播放一個聲音文件。 在很多情況下,這個數(shù)據(jù)類型可以從URI里推斷出來-尤其是content:URIs, 這意味著數(shù)據(jù)被存放在設(shè)備上而且由一個內(nèi)容提供者控制著。(參閱separate discussion on content providers). 但類型可以在意圖對象里顯示的設(shè)置。setData()方法指定數(shù)據(jù)只能為一個URI,setType()指定它只能是一個MIME類型, 而setDataAndType()指定它同時為URI和MIME類型。URI通過getData()讀取,類型則通過getType().
目錄Category 一個包含關(guān)于應(yīng)該處理這個意圖的組件的附加信息的字符串。任意數(shù)目的類別描述可以被放到一個意圖對象里。和動作一樣,意圖類定義若干類別常量,包含如下這些:
查閱Intent類描述可獲取類別的完整列表。 addCategory()方法在一個意圖對象中添加了一個目錄,removeCategory()刪除之前添加的目錄,而getCategories()可以獲取當(dāng)前對象的所有類別。
應(yīng)該遞交給意圖處理組件的附加信息鍵-值對。就像一些動作伴隨著特定的數(shù)據(jù)URIs類型,一些動作則伴隨著特定的附加信息。比如,一個ACTION_TIMEZONE_CHANGED意圖有一個“時區(qū)”附加信息用來區(qū)別新的時區(qū),而ACTION_HEADSET_PLUG有一個“狀態(tài)”附加字段表明耳機(jī)有沒有插著,以及一個“名字”附加信息來表示耳機(jī)的類型。如果你想要創(chuàng)建一個SHOW_COLOR動作,顏色的值將被設(shè)置在一個附加的鍵-值對中。 意圖對象有一系列的put...()方法來插入各種不同的附加數(shù)據(jù)和一個類似的用來讀取數(shù)據(jù)的get...()方法系列。這些方法與Bundle對象的方法相似。事實上,附加信息可以被當(dāng)作一個Bundle通過使用putExtras()和getExtras()方法安裝和讀取。 標(biāo)志Flags 各種類型的標(biāo)志. 許多標(biāo)志用來指示Android系統(tǒng)如何去加載一個活動(例如,哪個是這個活動應(yīng)該歸屬的任務(wù))和啟動后如何對待它(比如,它是否屬于當(dāng)前活動列表),所有這些列表都在意圖類中定義了。 Android系統(tǒng)以及這個平臺上的應(yīng)用程序利用意圖對象來發(fā)送源于系統(tǒng)的廣播以及激活系統(tǒng)定義的組件。要查閱如何組織一個意圖去激活一個系統(tǒng)組件,請咨詢引用中的意圖列表list of intents。
意圖解析Intent Resolution 意圖可以被分成兩組:
Android遞交一個顯式的意圖給一個指定目標(biāo)類的實例。意圖對象中的組件名稱唯一的確定哪個組件應(yīng)該獲取這個意圖。隱式意圖需要一個不同的策略。在沒有指定目標(biāo)的情況下,Android系統(tǒng)必須找到最合適的組件來處理這個意圖-單個活動或者服務(wù)來執(zhí)行這個請求動作或者一系列的廣播接收器來應(yīng)對廣播通告。 這是通過比較意圖對象的內(nèi)容和意圖過濾器,有可能接收意圖的組件相關(guān)結(jié)構(gòu)。過濾器公布一個組件具備的能力以及限定它能處理的意圖。他們使組件接收該公布類型的隱式意圖成為可能。如果一個組件沒有任何的意圖過濾器,那它只能接收顯式意圖。一個帶過濾器的組件可以同時接收顯式和隱式意圖。 當(dāng)一個意圖對象被一個意圖過濾器測試時,只有三個方面會被參考到: 動作 附加信息和標(biāo)志并不參與解析哪個組件接收一個意圖。
意圖過濾器Intent filters 為了通知系統(tǒng)它們可以處理哪些意圖,活動、服務(wù)和廣播接收器可以有一個或多個意圖過濾器。每個過濾器描述組件的一個能力,一系列組件想要接收的意圖。它實際上按照一個期望的類型來進(jìn)行意圖濾入,同時濾出不想要的意圖-但是只有不想要的隱式意圖會被濾出(那些沒有命名目標(biāo)的對象類)。一個顯式意圖總能夠被遞交給它的目標(biāo),而無論它包含什么。這種情況下過濾器不起作用。但是一個顯式意圖僅當(dāng)它能通過組件的一個過濾器時才可以被遞交到這個組件。 組件為它能做的每項工作,每個呈現(xiàn)給用戶的不同方面分有不同的過濾器。比如,范例記事本應(yīng)用程序中的主要活動有三個過濾器-一個是空白板,另一個是用戶可以查看、編輯、或選擇的一個指定的記事目錄,第三是在沒有初始目錄說明的情況下查找一個特定的記錄。一個意圖過濾器是IntentFilter類的一個實例。但是,由于Android系統(tǒng)在啟動一個組件前必須知道這個組件的能力,意圖過濾器通常不會用Java代碼來設(shè)置,而是在應(yīng)用程序清單文件(AndroidManifest.xml)中設(shè)置<intent-filter>元素。(有一個例外,通過調(diào)用Context.registerReceiver() 來注冊的廣播接收器的過濾器;它們是作為意圖過濾器對象而被直接創(chuàng)建的。
過濾器與安全Filters and security 不能信賴一個意圖過濾器的安全性。當(dāng)它打開一個組件來接收某些特定類型的隱式意圖,它并不能阻止以這個組件為目標(biāo)的顯式意圖。即使過濾器對組件要處理的意圖限制某些動作和數(shù)據(jù)源,總有人能把一個顯式意圖和一個不同的動作及數(shù)據(jù)源組合在一起,然后命名該組件為目標(biāo)。 一個過濾器和意圖對象有同樣的動作、數(shù)據(jù)以及類別字段。一個隱式意圖在過濾器的所有三個方面都被測試。為了遞交到擁有這個過濾器的組件,它必須通過所有這三項測試。即便只有一個不通過,Android系統(tǒng)都不會把它遞交給這個組件-至少以那個過濾器的標(biāo)準(zhǔn)而言。不過,由于一個組件可以包含多個意圖過濾器,一個不能通過其中一個組件過濾器的意圖可能在另外的過濾器上獲得通過。 三個測試詳細(xì)描述如下: 動作測試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> 如同例子所示,一個意圖對象只對單個動作命名,而一個過濾器可能列舉多個。列表不能為空;一個過濾器必須包含至少一個動作元素,否則它將阻塞所有的意圖。 為了通過這個測試,在意圖對象中指定的動作必須匹配過濾器中所列舉的動作之一。如果意圖對象或過濾器不指定一個動作,結(jié)果將如下: · 如果這個過濾器沒有列出任何動作,那意圖就沒有什么可匹配的,因此所有的意圖都會測試失敗。沒有意圖能夠通過這個過濾器。 · 另一方面,一個未指定動作的意圖對象自動通過這個測試-只要過濾器包含至少一個動作。 類別測試Category test 一個意圖過濾器<intent-filter>元素也列舉了類別作為子元素。比如: <intent-filter . . . > <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> . . . </intent-filter> 注意前面描述的動作和類別常量沒有在清單文件中使用。相反使用了完整的字符串。比如,對應(yīng)于前述CATEGORY_BROWSABLE常量,上面的例子里使用了"android.intent.category.BROWSABLE"字符串。類似的,字符串"android.intent.action.EDIT" 對應(yīng)于ACTION_EDIT常量。 對一個通過類別測試的意圖,每個意圖對象中的類別必須匹配一個過濾器中的類別。這個過濾器可以列舉另外的類別,但它不能遺漏任何在這個意圖中的類別。 因此,原則上一個沒有類別的意圖對象應(yīng)該總能夠通過測試,而不管過濾器里有什么。絕大部分情況下這個是對的。但有一個例外,Android把所有傳給startActivity()的隱式意圖當(dāng)作他們包含至少一個類別:"android.intent.category.DEFAULT" (CATEGORY_DEFAULT常量)。 因此,想要接收隱式意圖的活動必須在它們的意圖過濾器中包含"android.intent.category.DEFAULT"。(帶"android.intent.action.MAIN"和"android.intent.category.LAUNCHER"設(shè)置的過濾器是例外)。它們標(biāo)記那些啟動新任務(wù)和呈現(xiàn)在啟動屏幕的活動。它們可以在類別列表中包含"android.intent.category.DEFAULT",但不是必要的。) 可查閱后面的使用意圖匹配(Using intent matching)以獲得更多關(guān)于過濾器的信息。 數(shù)據(jù)測試Data test 就像動作和類別,一個意圖過濾器的數(shù)據(jù)規(guī)格被包含在一個子元素中。而且這個子元素可以出現(xiàn)多次或一次都不出現(xiàn)。例如: <intent-filter . . . > <data android:type="video/mpeg" android:scheme="http" . . . /> <data android:type="audio/mpeg" android:scheme="http" . . . /> . . . </intent-filter> 每個數(shù)據(jù)<data>元素可以指定一個URI和一個數(shù)據(jù)類型(MIME媒體類型)。有一些單獨的屬性-模式,主機(jī),端口和路徑-URI的每個部分: scheme://host:port/path 比如,在下面的URI里面, content://com.example.project:200/folder/subfolder/etc 模式是"內(nèi)容",主機(jī)是"com.example.project",端口是"200",路經(jīng)是"folder/subfolder/etc"。主機(jī)和端口一起組成URI鑒權(quán)(authority);如果未指定主機(jī),端口會被忽略。 這些屬性都是可選的,但彼此有依賴關(guān)系:一個授權(quán)要有意義,必須指定一個模式。一個路經(jīng)要有意義,必須同時指定模式和鑒權(quán)。 當(dāng)一個意圖對象中的URI被用來和一個過濾器中的URI規(guī)格比較時,它實際上比較的是上面提到的URI的各個部分。比如,如果過濾器僅指定了一個模式,所有那個模式的URIs和這個過濾器相匹配;如果過濾器指定了一個模式、鑒權(quán)但沒有路經(jīng),所有相同模式和鑒權(quán)的URIs可以匹配上,而不管它們的路經(jīng);如果過濾器指定了一個模式、鑒權(quán)和路經(jīng),只有相同模式、鑒權(quán)和路經(jīng)的URIs可以匹配上。當(dāng)然,一個過濾器中的路徑規(guī)格可以包含通配符,這樣只需要部分匹配即可。 數(shù)據(jù)<data>元素的類型屬性指定了數(shù)據(jù)的MIME類型。這在過濾器里比在URI里更為常見。意圖對象和過濾器都可以使用一個"*"通配符指定子類型字段-比如,"text/*"或者"audio/*"-指示任何匹配的子類型。 數(shù)據(jù)測試同時比較意圖對象和過濾器中指定的URI和數(shù)據(jù)類型。規(guī)則如下: a. 一個既不包含URI也不包含數(shù)據(jù)類型的意圖對象僅在過濾器也同樣沒有指定任何URIs和數(shù)據(jù)類型的情況下才能通過測試。 b. 一個包含URI但沒有數(shù)據(jù)類型的意圖對象僅在它的URI和一個同樣沒有指定數(shù)據(jù)類型的過濾器里的URI匹配時才能通過測試。這通常發(fā)生在類似于mailto:和tel:這樣的URIs上:它們并不引用實際數(shù)據(jù)。 c. 一個包含數(shù)據(jù)類型但不包含URI的意圖對象僅在這個過濾器列舉了同樣的數(shù)據(jù)類型而且也沒有指定一個URI的情況下才能通過測試。 d. 一個同時包含URI和數(shù)據(jù)類型(或者可從URI推斷出數(shù)據(jù)類型)的意圖對象可以通過測試,如果它的類型和過濾器中列舉的類型相匹配的話。如果它的URI和這個過濾器中的一個URI相匹配或者它有一個內(nèi)容content:或者文件file: URI而且這個過濾器沒有指定一個URI,那么它也能通過測試。換句話說,一個組件被假定為支持content:和file: 數(shù)據(jù)如果它的過濾器僅列舉了一個數(shù)據(jù)類型。 如果一個意圖可以通過不止一個活動或服務(wù)的過濾器,用戶可能會被詢問要激活那個組件。如果沒有發(fā)現(xiàn)目標(biāo)對象將會出現(xiàn)異常。
上面描述的數(shù)據(jù)測試的最后一個規(guī)則(d),表達(dá)了這樣一個期望即組件能夠從文件或內(nèi)容提供者中獲取本地數(shù)據(jù)。因此,它們的過濾器可以只列舉一個數(shù)據(jù)類型而不需要顯式的命名content:和file:模式。這是一個典型情況。比如,一個如下的數(shù)據(jù)<data>元素,告訴Android這個組件能從內(nèi)容提供者獲取圖片數(shù)據(jù)并顯示: <data android:type="image/*" /> 既然大多數(shù)可用數(shù)據(jù)是通過內(nèi)容提供者來分發(fā),那么過濾器最通常的配置就是指定一個數(shù)據(jù)類型而不指定URI。另外一個通用的配置是帶有一個模式和數(shù)據(jù)類型的過濾器。比如,一個如下的數(shù)據(jù)<data>元素告訴Android可以從網(wǎng)絡(luò)獲取視頻數(shù)據(jù)并顯示: <data android:scheme="http" android:type="video/*" /> 比如,想一下,當(dāng)用戶點擊網(wǎng)頁上的一個鏈接時瀏覽器做了什么。它首先試圖去顯示這個數(shù)據(jù)(如果這個鏈接指向一個HTML頁面)。如果它不能顯示這個數(shù)據(jù),它會把一個顯式意圖和一個模式、數(shù)據(jù)類型組成整體然后嘗試啟動一個可以處理這個工作的活動。如果沒有接受者,它將要求下載管理器來下載數(shù)據(jù)。這讓它處于內(nèi)容提供者的控制下,以便一個潛在的更大的活動池可以做出反應(yīng)。 大多數(shù)應(yīng)用程序同樣有一個方法去啟動刷新,而不包含任何特定數(shù)據(jù)的引用。能初始化應(yīng)用程序的活動擁有指定動作為"android.intent.action.MAIN"的過濾器。如果它們表述在應(yīng)用程序啟動器中,那它們同樣指定了"android.intent.category.LAUNCHER"類別: <intent-filter . . . > <action android:name="code android.intent.action.MAIN" /> <category android:name="code android.intent.category.LAUNCHER" /> </intent-filter>
通過意圖過濾器匹配的意圖不僅是為了發(fā)現(xiàn)要激活的目標(biāo)組件,而且為了發(fā)現(xiàn)這個設(shè)備上的一系列組件的某些東西。比如,Android系統(tǒng)通過查找符合條件的所有活動(需要包含指定了動作"android.intent.action.MAIN"和"android.intent.category.LAUNCHER"類別的意圖過濾器,如前面章節(jié)所述)來產(chǎn)生應(yīng)用程序啟動器,也就是用戶可用程序的前置屏幕。然后它顯示在這個啟動器里的這些活動的圖標(biāo)和標(biāo)簽。類似的,它通過查找其過濾器配有"android.intent.category.HOME"元素的活動來發(fā)現(xiàn)桌面。 你的應(yīng)用程序可以用類似的方式使用意圖匹配。PackageManager有一系列的查詢query…()方法可以接收一個特定的意圖,以及相似的一個解析resolve…()方法系列可以確定應(yīng)答意圖的最佳組件。比如,queryIntentActivities()返回一個所有活動的列表,而queryIntentServices()返回一個類似的服務(wù)列表。兩個方法都不會激活組件;它們僅僅列舉能應(yīng)答的。對于廣播接收者,有一個類似的方法queryBroadcastReceivers()。 |
|