Android開發者指南-意圖和意圖過濾器

意圖和意圖過濾器

一個程序的三個核心組件——活動、服務和廣播接收者——通過被稱爲意圖(intent)的消息來被激活。意圖消息是一種在同一個或不同程序的組件間的運行後綁定機制。意圖自身,即一個Intent對象,是包含了對於要執行的操作的抽象描述的被動數據結構——或者,對於廣播來說,是對於已發生正在被播報的事件的描述。對於向不同類型的組建傳遞意圖,有不同的機制:

  • 一個Intent對象被傳遞給Context.startActivity()或Activity.startActivityForResult()以啓動一個活動,或是讓一個已存在的活動進行新的工作。(它也可以被傳遞給Activity.setResult()使其向調用了startActivityForResult()的活動返回信息。)
  • 一個Intent對象被傳遞給Context.startService()以初始化一個服務或向正在運行的服務發出新的指令。同樣的,一個意圖可以被傳遞給Context.bindService()以在進行調用的組件和目標服務間建立聯繫。如果服務不在運行,還可以選擇初始化該服務。
  • 被傳遞給任何廣播方法(例如Context.sendBroadcast()、Context.sendOrderedBroadcast()或Context.sendStickyBroadcast())的Intent對象將被髮送給所有相關的廣播接收者。很多種類的廣播在系統代碼中被創建。

在任何一種情況下,Android系統將尋找合適的活動、服務或廣播接收者來響應意圖,在必要時對它們進行初始化。這些消息系統之間沒有交集:廣播意圖僅被髮送給廣播接收者,而不會給活動或是服務。一個傳遞給startActivity()的意圖將僅被髮送給一個活動,而不會是服務或廣播接收者,依此類推。

本文檔將首先對Intent對象進行描述。之後介紹Android用以映射意圖至組建的規則——它如何瞭解哪一個組建應當接收意圖消息。對於沒有顯式地命名目標組件的意圖,這個過程將包括使用與可能對象關聯的意圖過濾器(intent filter)來測試Intent對象。

Intent對象

Intent對象是一捆數據。它包含了接受該意圖的組件所感興趣的信息(例如要採取的操作及操作的對象數據)及Android系統所需的信息(例如要處理該意圖的組件類別及如何啓動目標活動的指令)。原則上,它包含以下內容:

組件名稱

應當處理該意圖的組件名稱。這部分是一個ComponentName對象——一種目標組件的完整類名(例如“com.example.project.app.FreneticActivity”)和組件所在程序的manifest文件中的包名稱組(例如“com.example.project”)的組合。組件名稱的包部分和manifest中的包名稱組不一定要相同。

組件名稱是可選的。如果被設定,Intent對象將被髮送給指定類的實例。如果沒有設定,Android使用Intent對象中的其他信息來選擇合適的目標——參見本文檔中之後的“意圖解決方案”。

組件名稱通過setComponent()、setClass()或setClassName()來設定,通過getComponent()來讀取。

行爲

命名了要被執行的行爲的字符串——或者,對於廣播的意圖來說將要發生並被報告的行爲。Intent類定義了很多行爲常量,包括:

常量 目標組件 行爲
ACTION_CALL 活 動 初始化一次電話呼叫。
ACTION_EDIT 活 動 向用戶顯示數據以供編輯。
ACTION_MAIN 活 動 作爲一個任務(task)的首活動啓動,不包含數據輸入且沒有返回的輸出。
ACTION_SYNC 活 動 以移動設備上的數據同步服務器上的數據。
ACTION_BATTERY_LOW 廣播 接收者 電量低的警告。
ACTION_HEADSET_PLUG 廣播接收者 耳機插入或是拔出設備。
ACTION_SCREEN_ON 廣播接收者 屏幕被打開。
ACTION_TIMEZONE_CHANGED 廣播接收者 時區設定被改變。

參見Intent類的描述以獲知通用行爲的預定義常量列表。其他的行爲在Android API的其他部分被定義。也可以爲激活自己的程序中的組件而定義自有的行爲字符串。它們應當包含作爲前綴的程序包——例如:“com.example.project.SHOW_COLOR”

行爲很大程度上決定了意圖剩餘部分的結構——特別是數據和額外信息——就好像是一個方法的名稱決定了一組參數和返回值。因此,最好儘可能具體地使用行爲名稱,將它們緊密地和意圖的其他部分配對。換言之,不要單獨定義一個行爲,而是要爲組件可以處理的Intent對象定義一整套的協議。

一個Intent對象的行爲通過setAction()方法設置,通過getAction()來讀取。

數據

將被執行操作的數據的URI及數據的MIME類型。不同的行爲和不同的數據規格相對應。例如,如果行爲域是ACTION_EDIT,那數據域就需要包含要被顯示以編輯的文檔的URI。如果行爲是ACTION_CALL,那數據域需要是一個要撥打的號碼的tel: URI。同樣地,如果行爲是ACTION_VIEW且數據域是http: URI,那接收方活動將會下載或顯示該URI指向的任何數據。

在配對一個意圖和一個有能力處理其數據的組件時,知道數據的類型(即它的MIME類型)以及URI是很重要的。例如,一個可以顯示圖像數據的組建不應被調用來播放音頻文件。

在許多情況下,數據類型可以由URI獲知——特別是content: URI,它表明數據位於設備中並由一個內容提供者所控制(參見另外的關於內容提供者的解說)。不過也可以在Intent對象內顯式地設定類型。setData()方法以僅URI來指定數據,setType()僅以MIME類型來指定數據,setDataAndType()同時以URI和MIME類型來指定數據。URI通過getData()讀取,類型由getType()讀取。

類別

一個包含了應該由哪一種類型的組件來處理意圖的額外信息的字符串。Intent對象中可以存放任何數量的類別描述。和行爲一樣,Intent類定義了一些類別常量,其中包括:

常量 意義
CATEGORY_BROWSABLE 目標活動可以安全地被瀏覽器用於顯示某一鏈接指向的數據——例如,一幅圖像或是一條電子郵件消息。
CATEGORY_GADGET 該活動可以被另一個持有小部件(gadget)的活動嵌於內部。
CATEGORY_HOME 該活動顯示主界面(home screen)這一用戶啓動設備或按下HOME鍵後首先看到的畫面。
CATEGORY_LAUNCHER 該活動是一個任務中的首活動,被列於應用程序啓動器的首級。
CATEGORY_PREFERENCE 目標活動是一個偏好設置面板。

 參見Intent類的描述以獲知完整的類別列表。

addCategory()方法將一個類別存入一個Intent對象,removeCategory()則刪除之前添加的一個類別,getCategories()用於獲取當前在意圖對象內的整個類別集。

額外信息

關於需要被傳遞給要處理意圖的組件的其他信息的鍵值對(key-value pair)。如同一些行爲與特定類型的數據URI相配對,另一些會與特定的額外信息相配對。例如,一個ACTION_TIMEZONE_CHANGED意圖有一個額外信息“time-zone”來識別新的時區,ACTION_HEADSET_PLUG則有一個額外信息“state”來識別耳機是被插入還是拔出,另外還有額外信息“name”來識別耳機的類型。如果想要設計一個SHOW_COLOR行爲,顏色的值將被設置於一個額外的鍵值對之中。

Intent對象有一系列的put…()方法來插入不同類型的額外數據,類似地有一組get…()方法來讀取這些值。這些方法和Bundle對象中的相類似。事實上,額外信息可以如同一個Bundle那樣通過putExtras()和getExtras()方法來被設置和讀取。

旗標

各種類型的旗標。其中很多指示了Android系統如何去啓動一個活動(例如,該活動應該屬於哪一個任務)以及在啓動後應該如何處理它(例如,它是否應該屬於最近活動列表)。所有的這些旗標在Intent類中被定義。

Android系統和該平臺的應用程序使用Intent對象來發出系統創建的廣播及激活系統定義的組件。要了解如果構建一個意圖以激活一個系統組件,參見附錄中的意圖列表。

意圖的解決方案

意圖可以被分爲兩類:

  • 顯式意圖通過其名稱指定目標組件(前面提到過,該組件的名稱域有一個值集)。因爲組件的名稱通常不會被其他程序的開發者知道,顯式的意圖往往被用於程序內部消息——例如一個活動啓動一個附屬的服務或是一個平級的活動。
  • 隱式意圖不會命名目標(組件的名稱域是空白的)。隱式意圖常用於激活其他活動中的組件。

Android將一個顯式意圖傳遞給所指定的目標類的一個實例。只有Intent對象中的組件名稱會影響到意圖最終將被傳送給哪一個組件。

對於隱式意圖需要一種不同的策略。由於沒有指定的目標,Android系統必須找到最爲合適的那一組件(或那一些組件)——一個單獨的執行所請求的行動的活動或服務,或是一組廣播接收者來響應廣播通告。系統通過比較Intent對象的內容和意圖過濾器(和組件相關聯的用以接收潛在意圖的結構)來實現這一目的。過濾器告知了一個組件的功能並界定了它可以處理的意圖。它們使得組件可能接收所告知的類型的隱式意圖。如果一個組件沒有任何的意圖過濾器,它將只能接收顯示意圖。一個擁有意圖過濾器的組件可以同時接收顯式和隱式意圖。

當一個Intent對象被意圖過濾器檢查時只有三個方面會被考慮:

行爲

數據(同時包括URI和數據類型)

類別

額外信息和旗標不會影響到決定由哪個組件接收意圖。

意圖過濾器(Intent filter)

爲了告知系統它可以處理哪些隱式意圖,活動、服務和廣播接收者都可以擁有一個或多個意圖過濾器。每一個過濾器將描述組件的某一種功能以及該組件要接收的意圖集。它有效地過濾下所需的類型,排除不需要的意圖——不過僅能排除不需要的(未命名目標類的)隱式意圖。顯式意圖將始終被傳遞給其目標而無論它包含什麼內容;過濾器不會起作用。然而一個隱式意圖就只有在通過了組件的過濾器之後才能夠被傳遞給組件。

一個組件對每一類它可以處理的工作及用戶所能看見的每一種形式分別有着不同的過濾器。例如,範例中Note Pad程序的NoteEditor活動有兩個過濾器——一個用於創建用戶可以查看並編輯的特定便筏,另一個用於啓動一個空的用戶可以輸入並保存的新的便筏。(Note Pad的所有的過濾器將會在之後的Note Pad範例一節中詳細說明。)

一個意圖過濾器是IntentFilter類的一個實例。不過因爲Android系統必須在啓動一個組件之前知道它所具有的功能,所以意圖過濾器一般不在Java代碼中使用,而是作爲程序的manifest文件中(AndroidManifest.xml)的<intent-filter>元素。(不過有一個例外是廣播接收者的過濾器是通過調用Context.registerReceiver()動態地在代碼中註冊的;它們被作爲IntentFilter對象直接創建。)

一個過濾器有着和Intent對象中的行爲、數據和類別域相對應的域。一個隱式意圖將檢測過濾器的所有這三個內容。要被傳遞給某個組件,一個意圖必須通過該組件的過濾器的所有三項檢測。即使只有一項不滿足,Android系統也不會將該意圖傳遞給組件——至少不會是基於過濾器傳遞給組件。不過,由於一個組件可以有多個意圖過濾器,一個意圖如果無法通過其中一個過濾器還是有可能能通過其他的的。

過濾器和安全

意圖過濾器不能確保安全性。儘管它只讓一個組件接受特定類型的隱式意圖,它無法阻止顯式意圖指向目標組件。即使過濾器約束了意圖使其只能使用將會被請求處理特定的行爲和數據源的組件,一些人仍然可以將一個顯式意圖與不同的行爲和數據源結合在一起,並將目標以組件的名稱來命名。

下面將詳細描述三種檢測是如何進行的:

行爲檢測

manifest文件中的一個<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對象中指定的行爲必須與過濾器所列出的行爲之一相匹配。如果該對象或是過濾器沒有指定一個行爲,就會有下面的結果:

  • 如果過濾器沒有列出任何行爲,就沒有行爲可以與意圖相匹配,所有的意圖都無法通過檢測。沒有意圖可以通過過濾器。
  • 另一方面,如果Intent對象沒有指定任何的行爲,它將自動通過檢測——只要過濾器含有一個或以上的行爲。

類別檢測

<intent-filter>也會將類別作爲子元素列出。例如:

<intent-filter . . . >
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    . . .
</intent-filter>

注意之前描述過的表示行爲和類型的常量不在mainfest文件中被使用。而是使用完整的字符串值。例如,在範例中的“android.intent.category.BROWSABLE”字符串和文檔之前提到的CATEGORY_BROWSABLE常量相對應。類似地,字符串“android.intent.action.EDIT”ACTION_EDIT常量相對應。

一個意圖要通過類別檢測,Intent對象中的每一個類別都必須與過濾器中的某個類別相匹配。過濾器可以列出其他更多的類別,但不能省略任何一個意圖中含有的類別。

原則上,所以說,一個不含有類別的Intent對象,無論過濾器中有哪些類別,總是能夠通過這項檢測。這通常是正確的。不過,有一個例外,Android把所有傳遞給startActivity()的隱式意圖視爲它們包含了至少這樣一個類別:“android.intent.category.DEFAULT”CATEGORY_DEFAULT常量)。因此,要接受隱式意圖的活動必須在其意圖過濾器中包含有“android.intent.category.DEFAULT”。(設有“android.intent.action.MAIN”“android.intent.category.LAUNCHER”的過濾器不在此範圍之中。它們將活動標記爲了新任務的開始,並顯示在應用啓動器(launcher)屏幕上。它們可以在類別列表中包含“android.intent.category.DEFAULT”,不過這並不是必須的。)參見之後的“使用意圖匹配”來進一步瞭解這些過濾器。

數據檢測

如同行爲和類別,意圖過濾器的的數據類型也是作爲子元素保存的。而且和它們一樣,這些子元素可以出現多次或完全不出現。例如:

<intent-filter . . . >
    <data android:mimeType="video/mpeg" android:scheme="http" . . . /> 
    <data android:mimeType="audio/mpeg" android:scheme="http" . . . />
    . . .
</intent-filter>

每一個<data>元素可以指定一個URI和數據類型(MINE媒體類型)。URI的每一個部分由不同的屬性——模式(scheme)、主機(host)、接口(port)和路徑(path)。

scheme://host:port/path

例如,下面的URI,

content://com.example.project:200/folder/subfolder/etc

其模式是“content”,主機是“com.example.project”,接口是“200″,路徑是“folder/subfolder/etc”。主機和接口共同構成了URI的授權;如果沒有指定主機,那麼接口將被忽略。

每一個屬性都是可選的,但並非與其他屬性相互獨立:要讓一個授權變得有意義,必須指定一個模式。要讓路徑具有意義,必須指定一個模式和一個授權。

當一個Intent對象中的URI和過濾器中的URI相比較時,僅比較過濾器中所包含的URI部分。例如,如果一個過濾器僅指定了一個模式,那所有具有這個模式的URI就與過濾器相匹配。如果過濾器指定了一個模式及一個授權但是沒有指定路徑,那麼無論是什麼路徑,具有相同模式和授權的URI將得到匹配。如果過濾器指定了模式、授權和路徑,那就只有具有相同模式、授權和路徑的URI得到匹配。不過,過濾器中指定的路徑可以包含通配符以僅僅限定部分路徑。

一個<data>元素的類型(type)屬性指定了數據的MINE類型。在過濾器中這比URI更爲常見。Intent對象和過濾器都可以用”*”通配符作爲子類別域來標識子類別匹配,例如“text/*”“audio/*”

數據檢測同時將Intent對象中的URI和數據類型與過濾器中的URI和數據類型相比較。該過程遵循以下規則:

  • 不包含URI和數據類型的Intent對象只有在過濾器也不指定任何URI及數據類型時才能通過檢測。
  • 包含URI但不包含數據類型(且數據類型不能通過URI推斷出來)的Intent對象只有在其URI與過濾器中的URI相匹配且過濾器同樣沒有指定類型時才能通過檢測。這是僅僅在類似mailto:和tel:這樣沒有指向實際數據的URI時纔會出現的情況。
  • 包含數據類型但不包含URI的Intent對象只有在過濾器列出了相同的數據類型而沒有指定URI時才能通過檢測。
  • 同時包含有URI和數據類型(或URI可以推導出數據類型)的Intent對象在其數據類型與過濾器中列出的類型相匹配時通過檢測的數據類型部分。當其URI與過濾器中的URI相匹配,或,其具有一個content:file:URI並且過濾器沒有指定URI時,通過測試的URI部分。換言之,如果過濾器僅列出了數據類型,一個組件將被假定支持content:file:數據。

如果一個意圖可以通過不止一個活動或服務的過濾器,用戶將被詢問要激活哪一個組件。如果沒有一個目標能被找到,則會拋出一個例外。

常見情況

上面數據檢測中的最後一條規則,說明了組件可以從一個文件或是內容提供者處獲取本地數據。因此,它們的過濾器可以僅僅列出數據類型而不需要顯式地命名content:file:模式。這是一種典型的情況。比如說,一個如下的<data>元素,告訴Android組件可以從內容提供者處獲取圖像數據並顯示:

<data android:mimeType="image/*" />

因爲大部分可用的數據由數據提供者分發,所以指定了數據類型而沒有指定URI的過濾器也許是最爲常見的了。

另一種常見的情況是過濾器指定了模式和數據類型。例如,如下的一個<data>元素告訴Android組件可以從網絡獲取視頻數據並播放:

<data android:scheme="http" android:type="video/*" />

試想,例如,瀏覽器類應用在用戶打開網頁上的一個鏈接後將做什麼。它首先嚐試顯示數據(如果該鏈接是一個HTML頁面的話)。如果它無法顯示數據,它將生成一個包含模式和數據類型的隱式意圖並試圖啓動一個可以處理該工作的活動。如果沒有合適的活動,它將請求下載管理器下載該數據。這處於內容提供者的控制之下,所以有大量的可能合適的活動(它們的過濾器僅指定了數據類型)將能夠做出響應。

大部分程序還有一種不需要任何指定數據引用的開始刷新的方法。能夠初始化一個程序的活動有着將行爲指定爲”android.intent.action.MAIN”的過濾器。如果它們需要被顯示在應用程序啓動器中的話,它們還需要被指定爲”android.intent.category.LAUNCHER”類別:

<intent-filter . . . >
    <action android:name="code android.intent.action.MAIN" />
    <category android:name="code android.intent.category.LAUNCHER" />
</intent-filter>

使用意圖匹配

將意圖與意圖過濾器相匹配不僅僅是爲了尋找要激活的某一目標組件,同時也是要獲取設備上的某一組組件的某些屬性。例如,Android系統通過查找所有具有指定了“android.intent.action.MAIN”行爲和“android.intent.category.LAUNCHER”類別的過濾器的活動(如前一節所示)來彈出應用程序啓動器這一列出了所有可供用戶啓動的程序的頂層屏幕內容。之後它在啓動器中顯示那些活動的圖標(icon)和標籤(label)。同樣地,它通過查找具有“android.intent.category.HOME”類別的過濾器的活動來顯示主界面。

程序可以以一種類似的方式來使用意圖匹配。PackageManager擁有一組query…()方法來返回所有可以接受某一特定意圖的組件,以及一系列的resolve…()方法來決定響應意圖最合適的組件。例如,queryIntentActivities()將返回一個列出了所有可以將意圖作爲參數傳遞的活動的列表,queryIntentServices()將返回一個類似作用的所有服務的列表。這兩種方法都不會激活組件;它們只是列出了那些可以做出響應的。對於廣播接收者,還有一個類似的queryBroadcastReceivers()方法。

記事本(Note Pad)範例

記事本範例允許用戶瀏覽筆記列表、查看列表中項目的詳細內容、編輯項目、向列表增加新項目。本節將分析在其manifest文件中聲明的意圖過濾器(如果正在離線使用SDK,可以在<sdk>/samples/NotePad/index.html下找到包括manifest文件在內的該範例程序的所有源文件。如果是在在線查看文檔,源文件則是在這裏的“教程和範例代碼”一節。

在manifest文件中,記事本程序聲明瞭三個活動,每一個都有至少一個意圖過濾器。它還聲明瞭一個內容提供者用以管理筆記數據。以下是整個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>

第一個活動,NoteList,和其他的活動不同,它要對一系列的筆記(筆記列表)進行操作而不是某條單獨的筆記。它通常將作爲用戶進入程序的初始界面。如其三個意圖過濾器所描述的,它能執行三項工作:

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

該過濾器聲明瞭Note Pad程序的主入口。標準MAIN行爲是一種不需要Intent中的其他信息(比如不需要數據類型)的入口(entry point),LAUNCHER類別則說明該入口應該被列於應用程序啓動器中。

<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>

該過濾器聲明瞭這個活動可以對一列筆記進行何種操作。它可以允許用戶查看或編輯筆記(通過VIEWEDIT行爲),或是從列表中選取一條特定筆記(通過PICK行爲)。

<data>元素的mimeType屬性指定了這些行爲要執行操作的數據類型。它表明了這個活動可以從一個持有Note Pad數據的內容提供器(vnd.google.note)中獲取一個指向零個或更多個項目(vnd.android.cursor.dir)的Cursor。啓動該活動的Intent對象包含一個content: URI用以指定活動應該打開的確切數據類型。

也請注意該過濾器中提供的DEFAULT類別,因爲Context.startActivity()和Activity.startActivityForResult()方法在意圖包含DEFAULT類別時將會對其進行處理——只有兩種例外:

  • 顯式地命名了目標活動的Intent
  • 包含了MAIN行爲和LAUNCHER類別的Intent

因此,所有的過濾器都需要DEFAULT類別——除非有MAIN行爲和LAUNCHER類別。(意圖過濾器不過濾顯式意圖)

<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>

該過濾器說明了這個活動可以返回一條用戶選中的筆記而不需要這條筆記所在的列表的相關信息。GET_CONTENT行爲類似於PICK行爲。兩種情況下活動都將返回用戶所選擇的筆記的URI。(將返回給調用了startActivityForResult()來啓動NoteList活動的那個活動。)不過現在,調用者指定了所需的數據類型而不是用戶要進行選擇的數據列表的類型。

數據類型,vnd.android.cursor.item/vnd.google.note,標明瞭該活動可以返回的數據的類型——一條單筆記的URI。通過所返回的URI,調用者可以從持有Note Pad數據(vnd.google.note)的內容提供其中獲取某一特定條目(vnd.android.cursor.item)的Cursor。

也就是說,前一個過濾器中的PICK行爲,數據類型標識出了活動可以顯示給用戶的數據的類型。而GET_CONTENT過濾器則標識出了活動可以返回給它的調用者的數據的類型。

有了這些能力,下面的意圖可以實現活動NotesList的功能:

行爲:android.intent.action.MAIN

不需要特定的數據就能啓動活動

行爲:android.intent.action.MAIN 

類別:android.intent.category.LAUNCHER

不需要特定的被選擇的數據就能啓動活動。這是實際上應用程序啓動器用來填充其最高層列表(top-level list)的意圖。所有具有匹配該行爲和類別的過濾器的活動將被添加至列表。

行爲:android.intent.action.VIEW 

數據:content://com.google.provider.NotePad/notes

請求活動顯示在content://com.google.provider.NotePad/notes下的所有筆記的列表。之後用戶可以瀏覽該列表並獲取列表中項目的信息。

行爲:android.intent.action.PICK 

數據:content://com.google.provider.NotePad/notes

請求活動顯示在content://com.google.provider.NotePad/notes下的筆記的列表。之後用戶可以從列表中選取一條筆記,活動將返回該條目的URI給啓動了NoteList活動的那個活動。

行爲:android.intent.action.GET_CONTENT 

數據類型:vnd.android.cursor.item/vnd.google.note

請求活動提供一條單獨的Note Pad數據。

第二個活動,NoteEditor,向用戶顯示一條單獨的筆記條目並允許對其進行編輯。根據其兩個意圖過濾器的描述,它可以進行兩項任務:

<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>

這個活動的第一個,也是主要的目的是使用戶與一條筆記交互,VIEW這條筆記或是EDIT它。(類別EDIT_NOTEEDIT的意義相同。)該意圖將包含匹配MIME類型vnd.android.cursor.item/vnd.google.note的數據的URI——也就是說,這條指定的筆記的URI。它通常是被NoteList活動的PICKGET_CONTENT行爲所返回的URI。

和之前一樣,該過濾器列出了DEFAULT類別因此這個活動可以被其他沒有顯式地指定NoteEditor類的意圖啓動。

<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>

這個活動的第二個目的是使用戶創建一條新的筆記並INSERT至現有的筆記列表中。該意圖包含了匹配MIME類型vnd.android.cursor.dir/vnd.google.note的數據的URI——即筆記將被插入的筆記列表的URI。

有了這些能力,下面的意圖可以實現NoteEditor活動的功能:

行爲:android.intent.action.VIEW 

數據:content://com.google.provider.NotePad/notes/ID

請求活動顯示由ID確定的筆記的內容。(關於content: URIs是如何指定一個羣組內的個別成員的,請參見“內容提供器”。)

行爲:android.intent.action.EDIT 

數據:content://com.google.provider.NotePad/notes/ID

請求活動顯示由ID確定的筆記的內容,並允許用戶對其進行編輯。如果用戶保存了修改,該活動將更新內容提供器中的筆記數據。

行爲:android.intent.action.INSERT 

數據:content://com.google.provider.NotePad/notes

請求活動在content://com.google.provider.NotePad/note的筆記列表中創建一個新的空筆記並允許用戶對其進行編輯。如果用戶保存了筆記,其URI將被返回給調用者。

最後一個活動,TitleEditor,允許用戶編輯筆記的標題。這可以通過直接調用該活動來實現(通過顯式地在Intent中設定該組件名稱),而不需要通過一個意圖過濾器。不過這裏主要關注如何對現存的數據進行其他操作:

<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>

這個活動的唯一的意圖過濾器使用了一個名爲”com.android.notepad.action.EDIT_TITLE“的自定義行爲。它必須像之前的VIEWEDIT行爲一樣通過一條指定的筆記調用(數據類型vnd.android.cursor.item/vnd.google.note)。不過,這個活動將顯示包含於筆記數據中的標題,而不是筆記內容自身。

另外爲了支持常見的DEFAULT類別,這個標題編輯器也支持其他兩種常見類別:ALTERNATIVE和SELECTED_ALTERNATIVE。這兩個類別表明了活動可以在一個選項菜單中顯示給用戶(就好象LAUNCHER類別表明了活動應當在應用程序啓動器中顯示給用戶一樣)。注意過濾器也支持一個顯式標籤(通過android:label=”@string/resolve_title”)以更好地控制用戶能夠看到當前被這個活動以其他行爲方式查看的數據的哪些內容。(關於這些類別和創建選項菜單的更多信息,請參見PackageManager.queryIntentActivityOptions()和Menu.addIntentOptions()方法。)

有了這些能力,下面的意圖就可以實現TitleEditor活動的功能:

行爲:com.android.notepad.action.EDIT_TITLE 

數據:content://com.google.provider.NotePad/notes/ID

請求活動顯示和筆記ID相關聯的標題,並允許用戶對其進行編輯。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章