[官方原文](https://developer.android.com/guide/components/intents-filters.html) ,由于需要科學上網,可以參考這里[Android Developers之Intent 和 Intent 過濾器](https://developer.android.google.cn/guide/components/intents-filters.html)
## Intent 和 Intent 過濾器
[Intent](https://developer.android.com/reference/android/content/Intent.html) 是一個**消息傳遞對象(信使)**,您可以使用它從其他[應用組件](https://developer.android.com/guide/components/fundamentals.html#Components)請求操作。盡管 Intent 可以通過多種方式**促進組件之間的通信**,但其基本用例主要包括以下三個:
* **啟動 Activit**y:
[Activity](https://developer.android.com/reference/android/app/Activity.html) 表示應用中的一個屏幕。通過將 Intent 傳遞給 [startActivity()](https://developer.android.com/reference/android/content/Context.html#startActivity(android.content.Intent)),您可以啟動新的 Activity 實例。Intent 描述了要啟動的 Activity,并攜帶了任何必要的數據。
如果您希望在 Activity 完成后收到結果,請調用 **startActivityForResult()**。在 該Activity 的 **onActivityResult()** 回調中,您的 Activity 將結果作為單獨的 Intent 對象接收。如需了解詳細信息,請參閱 Activity 指南。
* **啟動服務**:
**[Service](https://developer.android.com/reference/android/app/Service.html) 是一個不使用用戶界面而在后臺執行操作的組件**。通過將 Intent 傳遞給 [startService()](https://developer.android.com/reference/android/content/Context.html#startService(android.content.Intent)),您可以啟動服務執行一次性操作(例如,下載文件)。**Intent 描述了要啟動的服務,并攜帶了任何必要的數據,而且官方建議使用顯示Intent啟動服務(見下面的黃色注意內容)**。
如果服務旨在使用客戶端-服務器接口,則通過將 Intent 傳遞給 **bindService()**,您可以從其他組件綁定到此服務。如需了解詳細信息,請參閱[服務](https://developer.android.com/guide/components/services.html)指南。
* **傳遞廣播**:
**廣播是任何應用均可接收的消息**。系統將針對系統事件(例如:系統啟動或設備開始充電時)傳遞各種廣播。通過將 Intent 傳遞給 [sendBroadcast()](https://developer.android.com/reference/android/content/Context.html#sendBroadcast(android.content.Intent))、**sendOrderedBroadcast()** 或 **sendStickyBroadcast()**,您可以將廣播傳遞給其他應用。
### Intent 類型
Intent 分為兩種類型:
* **顯式 Intent**:按名稱(完全限定類名)指定要啟動的組件。 通常,您會在自己的應用中使用顯式 Intent 來啟動組件,這是因為您知道要啟動的 Activity 或服務的類名。例如,啟動新 Activity 以響應用戶操作,或者啟動服務以在后臺下載文件。
* **隱式 Intent** :**不會指定特定的組件,而是聲明要執行的常規操作,從而允許其他應用中的組件來處理它**。 例如,如需在地圖上向用戶顯示位置,則可以使用隱式 Intent,請求另一具有此功能的應用在地圖上顯示指定的位置。
創建顯式 Intent 啟動 Activity 或服務時,系統將立即啟動 Intent 對象中指定的應用組件。
創建隱式 Intent 時,Android 系統通過將 Intent 的內容與在設備上其他應用的[清單文件](https://developer.android.com/guide/topics/manifest/manifest-intro.html)中聲明的 Intent 過濾器進行比較,從而找到要啟動的相應組件。 如果 Intent 與 Intent 過濾器匹配,則系統將啟動該組件,并向其傳遞 Intent 對象。 如果多個 Intent 過濾器兼容,則系統會顯示一個對話框,支持用戶選取要使用的應用。
Intent 過濾器是應用清單文件中的一個表達式,它指定該組件要接收的 Intent 類型。 例如,通過為 Activity 聲明 Intent 過濾器,您可以使其他應用能夠直接使用某一特定類型的 Intent 啟動 Activity。同樣,如果您沒有為 Activity 聲明任何 Intent 過濾器,則 Activity 只能通過顯式 Intent 啟動。
>[warning] **注意**:為了確保應用的安全性,啟動 Service 時,請始終使用顯式 Intent,且不要為服務聲明 Intent 過濾器。使用隱式 Intent 啟動服務存在安全隱患,因為您無法確定哪些服務將響應 Intent,且用戶無法看到哪些服務已啟動。從 Android 5.0(API 級別 21)開始,如果使用隱式 Intent 調用 bindService(),系統會引發異常。

圖 1. 隱式 Intent 如何通過系統傳遞以啟動其他 Activity 的圖解:
* [1] Activity A 創建包含操作描述的 Intent,并將其傳遞給 startActivity()。
* [2] Android 系統搜索所有應用中與 Intent 匹配的 Intent 過濾器。 找到匹配項之后,
* [3] 該系統通過調用匹配 Activity(Activity B)的 onCreate() 方法并將其傳遞給 Intent,以此啟動匹配 Activity。
### 構建 Intent
Intent 對象攜帶了 Android 系統用來確定要啟動哪個組件的信息(例如,準確的組件名稱或應當接收該 Intent 的組件類別),以及收件人組件為了正確執行操作而使用的信息(例如,要采取的操作以及要處理的數據)。
Intent 中包含的主要信息如下:
- **組件名稱( ComponentName)**
要啟動的組件名稱。
這是**可選項**,**但也是構建顯式Intent 的一項重要信息**,這意味著 Intent 應當僅傳遞給由組件名稱定義的應用組件。 如果**沒有組件名稱,則 Intent 是隱式**的,且系統將根據其他 Intent 信息(例如,以下所述的操作、數據和類別)決定哪個組件應當接收 Intent。 因此,**如需在應用中啟動特定的組件,則應指定該組件的名稱**。
>[info] 注:啟動 Service 時,您應**始終指定組件名稱**(啟動服務最好使用顯示Intent)。 否則,您無法確定哪項服務會響應 Intent,且用戶無法看到哪項服務已啟動。
[Intent](https://developer.android.com/reference/android/content/Intent.html) 的這一字段是一個 [ComponentName](https://developer.android.com/reference/android/content/ComponentName.html) 對象,您可以使用目標組件的完全限定類名指定此對象,其中包括應用的軟件包名稱。 例如, `com.example.ExampleActivity`。您可以使用 [setComponent()](https://developer.android.com/reference/android/content/Intent.html#setComponent(android.content.ComponentName))、setClass()、setClassName()或 Intent 構造函數設置組件名稱。
* * * * *
- **操作(Action)**
**指定要執行的通用操作(例如,“查看”或“選取”)的字符串。**
**對于廣播 Intent,這是指已發生且正在報告的操作**。操作在很大程度上決定了其余 Intent 的構成,特別是數據和 extra 中包含的內容。
您可以指定自己的操作,供 Intent 在您的應用內使用(或者供其他應用在您的應用中調用組件)。但是,您通常應該使用由 Intent 類或其他框架類定義的操作常量。以下是一些用于啟動 Activity 的常見操作:
[**ACTION_VIEW**](https://developer.android.com/reference/android/content/Intent.html#ACTION_VIEW)
如果您擁有一些某項 Activity 可向用戶顯示的信息(例如,要使用圖庫應用查看的照片;或者要使用地圖應用查看的地址),請使用 Intent 將此操作與 [startActivity()](https://developer.android.com/reference/android/content/Context.html#startActivity(android.content.Intent)) 結合使用。
[**ACTION_SEND**](https://developer.android.com/reference/android/content/Intent.html#ACTION_SEND)
這也稱為“共享”Intent。如果您擁有一些用戶可通過其他應用(例如,電子郵件應用或社交共享應用)共享的數據,則應使用 Intent 將此操作與 [startActivity()](https://developer.android.com/reference/android/content/Context.html#startActivity(android.content.Intent)) 結合使用。
有關更多定義通用操作的常量,請參閱 Intent 類參考文檔。 其他操作在 Android 框架中的其他位置定義。例如,對于在系統的設置應用中打開特定屏幕的操作,將在 [Settings](https://developer.android.com/reference/android/provider/Settings.html) 中定義。
您可以使用 [setAction()](https://developer.android.com/reference/android/content/Intent.html#setAction(java.lang.String)) 或 Intent 構造函數為 Intent 指定操作。
如果定義自己的操作,請確保將應用的軟件包名稱作為前綴。 例如:
~~~
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
~~~
* * * * *
- **數據(Data)**
引用待操作數據和/或該數據 MIME 類型的 URI([Uri](https://developer.android.com/reference/android/net/Uri.html) 對象)。**提供的數據類型通常由 Intent 的操作決定**。例如,如果操作是 [ACTION_EDIT](https://developer.android.com/reference/android/content/Intent.html#ACTION_EDIT),則數據應包含待編輯文檔的 URI。
創建 Intent 時,除了指定 URI 以外,**指定數據類型(其 MIME 類型)往往也很重要**。例如,能夠顯示圖像的 Activity 可能無法播放音頻文件,即便 URI 格式十分類似時也是如此。因此,**指定數據的 MIME 類型有助于 Android 系統找到接收 Intent 的最佳組件**。**但有時,MIME 類型可以從 URI 中推斷得出**,特別當數據是 **content: URI** 時尤其如此。這表明數據位于設備中,且由 [ContentProvider ](https://developer.android.com/reference/android/content/ContentProvider.html)控制,這使得數據 MIME 類型對系統可見。
**要僅設置數據 URI,請調用 [setData()](https://developer.android.com/reference/android/content/Intent.html#setData(android.net.Uri))。 要僅設置 MIME 類型,請調用 [setType()](https://developer.android.com/reference/android/content/Intent.html#setType(java.lang.String))。如有必要,您可以使用 setDataAndType() 同時顯式設置二者。**
>[info] 注意:若要同時設置 URI 和 MIME 類型,請勿調用 setData() 和 setType(),因為它們會互相抵消彼此的值。請始終使用 setDataAndType() 同時設置 URI 和 MIME 類型。
* * * * *
- **類別(category)**
**一個包含應處理 Intent 組件類型的附加信息的字符串。 您可以將任意數量的類別描述放入一個 Intent 中,但大多數 Intent 均不需要類別**。 以下是一些常見類別:
[**CATEGORY_BROWSABLE**](https://developer.android.com/reference/android/content/Intent.html#CATEGORY_BROWSABLE)
目標 Activity 允許本身通過網絡瀏覽器啟動,以顯示鏈接引用的數據,如圖像或電子郵件。
[**CATEGORY_LAUNCHER**](https://developer.android.com/reference/android/content/Intent.html#CATEGORY_LAUNCHER)
該 Activity 是任務的初始 Activity,在系統的應用啟動器中列出。
有關類別的完整列表,請參閱 [Intent](https://developer.android.com/reference/android/content/Intent.html) 類描述。
您可以使用 [addCategory() ](https://developer.android.com/reference/android/content/Intent.html#addCategory(java.lang.String))指定類別。
以上列出的這些屬性(**組件名稱、操作、數據和類別)表示 Intent 的既定特征。 通過讀取這些屬性,Android 系統能夠解析應當啟動哪個應用組件。**
但是,Intent 也有可能會一些攜帶不影響其如何解析為應用組件的信息。 Intent 還可以提供:
* * * * *
- **Extra(附加信息)**
攜帶完成請求操作所需的附加信息的鍵值對。正如某些操作使用特定類型的數據 URI 一樣,有些操作也使用特定的 extra。
您可以使用各種 **putExtra**()方法添加 extra 數據,**每種方法均接受兩個參數:鍵名和值**。您還可以**創建一個包含所有 extra 數據的 [Bundle](https://developer.android.com/reference/android/os/Bundle.html) 對象,然后使用 [putExtras()](https://developer.android.com/reference/android/content/Intent.html#putExtras(android.content.Intent)) 將Bundle 插入 Intent 中。**
例如,使用 ACTION_SEND 創建用于發送電子郵件的 Intent 時,可以使用 [EXTRA_EMAIL](https://developer.android.com/reference/android/content/Intent.html#EXTRA_EMAIL) 鍵指定“目標”收件人,并使用 [EXTRA_SUBJECT](https://developer.android.com/reference/android/content/Intent.html#EXTRA_SUBJECT) 鍵指定“主題”。
Intent 類將為標準化的數據類型指定多個 EXTRA_* 常量。如需聲明自己的 extra 鍵(對于應用接收的 Intent),請確保將應用的軟件包名稱作為前綴。 例如:
~~~
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
~~~
* * * * *
- **標志(Flags)**
在 Intent 類中定義的、充當 Intent 元數據的標志。 標志可以指示 Android 系統如何啟動 Activity(例如,Activity 應屬于哪個[任務](https://developer.android.com/guide/components/tasks-and-back-stack.html)),以及啟動之后如何處理(例如,它是否屬于最近的 Activity 列表)。
如需了解詳細信息,請參閱 [setFlags()](https://developer.android.com/reference/android/content/Intent.html#setFlags(int)) 方法。
#### **顯式 Intent 示例**
顯式 Intent 是指用于啟動某個特定應用組件(例如,應用中的某個特定 Activity 或服務)的 Intent。 要創建顯式 Intent,請為 [Intent](https://developer.android.com/reference/android/content/Intent.html) 對象定義組件名稱 — Intent 的所有其他屬性均為可選屬性。
例如,如果在應用中構建了一個名為 DownloadService、旨在從網頁下載文件的服務,則可使用以下代碼啟動該服務:
~~~
// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);
~~~
Intent(Context, Class) 構造函數分別為應用和組件提供 [Context](https://developer.android.com/reference/android/content/Context.html) 和 [Class](https://developer.android.com/reference/java/lang/Class.html) 對象。因此,此 Intent 將顯式啟動該應用中的 DownloadService 類。
如需了解有關構建和啟動服務的詳細信息,請參閱[服務](https://developer.android.com/guide/components/services.html)指南。
#### **隱式 Intent 示例**
隱式 Intent 指定能夠在可以執行相應操作的設備上調用任何應用的操作。 如果您的應用無法執行該操作而其他應用可以,且您希望用戶選取要使用的應用,則使用隱式 Intent 非常有用。
例如,如果您希望用戶與他人共享您的內容,請使用 [ACTION_SEND](https://developer.android.com/reference/android/content/Intent.html#ACTION_SEND) 操作創建 Intent,并添加指定共享內容的 extra。 使用該 Intent 調用 startActivity() 時,用戶可以選取共享內容所使用的應用。
>[info] **注意**:用戶可能沒有任何應用處理您發送到 startActivity() 的隱式 Intent。如果出現這種情況,則調用將會失敗,且應用會崩潰。
>**要驗證 Activity 是否會接收 Intent,請對 Intent 對象調用 [resolveActivity()](https://developer.android.com/reference/android/content/Intent.html#resolveActivity(android.content.pm.PackageManager))**。
>如果結果為非空,則至少有一個應用能夠處理該 Intent,且可以安全調用 startActivity()。
>如果結果為空,則不應使用該 Intent。如有可能,您應停用發出該 Intent 的功能。
>**也可以調用PackageManager的resolveActivity()方法。針對Service和BroadcastReceiver,PackageManager同樣提供了類似的方法去獲得匹配的組件信息。**
~~~
// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");
// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
~~~
>[info] 注:在這種情況下,系統并沒有使用 URI,但已聲明 Intent 的數據類型,用于指定 extra 攜帶的內容。
調用 [startActivity()](https://developer.android.com/reference/android/content/Context.html#startActivity(android.content.Intent)) 時,系統將檢查已安裝的所有應用,確定哪些應用能夠處理這種 Intent(即:含 [ACTION_SEND](https://developer.android.com/reference/android/content/Intent.html#ACTION_SEND) 操作并攜帶“text/plain”數據的 Intent )。 如果只有一個應用能夠處理,則該應用將立即打開并為其提供 Intent。 如果多個 Activity 接受 Intent,則系統將顯示一個對話框,使用戶能夠選取要使用的應用。
### **強制使用應用選擇器**
**如果有多個應用響應隱式 Intent,則用戶可以選擇要使用的應用,并將其設置為該操作的默認選項。 如果用戶可能希望今后一直使用相同的應用執行某項操作(例如,打開網頁時,用戶往往傾向于僅使用一種網絡瀏覽器),則這一點十分有用。**
但是,**如果多個應用可以響應 Intent,且用戶可能希望每次使用不同的應用,則應采用顯式方式顯示選擇器對話框**。 選擇器對話框每次都會要求用戶選擇用于操作的應用(用戶無法為該操作選擇默認應用)。 例如,當應用使用 ACTION_SEND 操作執行“共享”時,用戶根據目前的狀況可能需要使用另一不同的應用,因此應當始終使用選擇器對話框,如圖 2 中所示。
:-: 
圖 2. 選擇器對話框。
要顯示選擇器,請使用 createChooser() 創建 Intent,并將其傳遞給 startActivity()。例如:
~~~
Intent sendIntent = new Intent(Intent.ACTION_SEND);
...
// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show the chooser dialog
Intent chooser = Intent.createChooser(sendIntent, title);
// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(chooser);
}
~~~
這將顯示一個對話框,其中有響應傳遞給 createChooser() 方法的 Intent 的應用列表,并且將提供的文本用作對話框標題。
### 接收隱式 Intent
要公布應用可以接收哪些隱式 Intent,請在[清單文件](https://developer.android.com/guide/topics/manifest/manifest-intro.html)中使用[ `<intent-filter>`](https://developer.android.com/guide/topics/manifest/intent-filter-element.html) 元素**為每個應用組件聲明一個或多個 Intent 過濾器**。每個 Intent 過濾器均根據 Intent 的操作、數據和類別指定自身接受的 Intent 類型。 **僅當隱式 Intent 可以通過 Intent 過濾器之一傳遞時,系統才會將該 Intent 傳遞給應用組件。**
>[info] 注:顯式 Intent 始終會傳遞給其目標,無論組件聲明的 Intent 過濾器如何均是如此。
**應用組件應當為自身可執行的每個獨特作業聲明單獨的過濾器**。*例如,圖像庫應用中的一個 Activity 可能會有兩個過濾器,分別用于查看圖像和編輯圖像*。 當 Activity 啟動時,它將檢查 Intent 并根據 Intent 中的信息決定具體的行為(例如,是否顯示編輯器控件)。
每個 Intent 過濾器均由應用清單文件中的 `<intent-filter>` 元素定義,并嵌套在相應的應用組件(例如,標簽`<activity>` 元素)中。 在 `<intent-filter>` 內部,您可以使用以下三個元素中的一個或多個指定要接受的 Intent 類型:
* [`<action>`](https://developer.android.com/guide/topics/manifest/action-element.html)
在 name 屬性中,**聲明接受的 Intent 操作**。該值**必須是操作的文本字符串值,而不是類常量**。
* [`<data>`](https://developer.android.com/guide/topics/manifest/data-element.html)
**使用一個或多個指定數據 URI 各個方面(scheme、host、port、path 等)和 MIME 類型的屬性,聲明接受的數據類型。**
* [`<category>`](https://developer.android.com/guide/topics/manifest/category-element.html)
在 name 屬性中,**聲明接受的 Intent 類別**。該值必**須是操作的文本字符串值,而不是類常量**。
>[info] 注:**為了接收隱式 Intent,必須將 [CATEGORY_DEFAULT](https://developer.android.com/reference/android/content/Intent.html#CATEGORY_DEFAULT) 類別包括在 Intent 過濾器中**。 方法 startActivity() 和 startActivityForResult() 將按照已申明 CATEGORY_DEFAULT 類別的方式處理所有 Intent。 如果未在 Intent 過濾器中聲明此類別,則隱式 Intent 不會解析為您的 Activity。
例如,以下是一個使用包含 Intent 過濾器的 Activity 聲明,當數據類型為文本時,系統將接收 ACTION_SEND Intent :
~~~
<activity android:name="ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
~~~
您可以創建一個包括多個 `<action>`、`<data>` 或 `<category>` 實例的過濾器。創建時,僅需確定組件能夠處理這些過濾器元素的任何及所有組合即可。
如需僅以操作、數據和類別類型的特定組合來處理多種 Intent,則需創建多個 Intent 過濾器。
系統通過將 Intent 與所有這三個元素進行比較,根據過濾器測試隱式 Intent。 **隱式 Intent 若要傳遞給組件,必須通過所有這三項測試**。如果 Intent 甚至無法匹配其中任何一項測試,則 Android 系統不會將其傳遞給組件。 **但是,由于一個組件可能有多個 Intent 過濾器,因此未能通過某一組件過濾器的 Intent 可能會通過另一過濾器**。如需了解有關系統如何解析 Intent 的詳細信息,請參閱下文的 [Intent 解析](https://developer.android.com/guide/components/intents-filters.html#Resolution)部分。
>[warning] **限制對組件的訪問**
> 使用 Intent 過濾器時,無法安全地防止其他應用啟動組件。 盡管 Intent 過濾器將組件限制為僅響應特定類型的隱式 Intent,但如果開發者確定您的組件名稱,則其他應用有可能通過使用顯式 Intent 啟動您的應用組件。**如果必須確保只有您自己的應用才能啟動您的某一組件,請針對該組件將 [exported](https://developer.android.com/guide/topics/manifest/activity-element.html#exported) 屬性設置為 "false"。**
>[info] **注意**:為了避免無意中運行不同應用的 Service(因為Service最好還是用顯示intent啟動),請始終使用顯式 Intent 啟動您自己的服務,且不必為該服務聲明 Intent 過濾器。
> 注:**對于所有 Activity,您必須在清單文件中聲明 Intent 過濾器**。但是,**廣播接收器的過濾器可以通過調用 registerReceiver() 動態注冊**。 稍后,您可以使用 [unregisterReceiver()](https://developer.android.com/reference/android/content/Context.html#unregisterReceiver(android.content.BroadcastReceiver)) 注銷該接收器。這樣一來,應用便可僅在應用運行時的某一指定時間段內偵聽特定的廣播。
#### **過濾器示例**
為了更好地了解一些 Intent 過濾器的行為,我們一起來看看從社交共享應用的清單文件中截取的以下片段。
~~~
<activity android:name="MainActivity">
<!-- This activity is the main entry, should appear in app launcher -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="ShareActivity">
<!-- This activity handles "SEND" actions with text data -->
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
<!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.SEND_MULTIPLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="application/vnd.google.panorama360+jpg"/>
<data android:mimeType="image/*"/>
<data android:mimeType="video/*"/>
</intent-filter>
</activity>
~~~
第一個 Activity MainActivity 是應用的主要入口點。當用戶最初使用啟動器圖標啟動應用時,該 Activity 將打開:
* [**ACTION_MAIN**](https://developer.android.com/reference/android/content/Intent.html#ACTION_MAIN) 操作指示這是**主要入口點**,且**不要求輸入任何 Intent 數據**。
* [**CATEGORY_LAUNCHER**](https://developer.android.com/reference/android/content/Intent.html#CATEGORY_LAUNCHER) 類別**指示此 Activity 的圖標應放入系統的應用啟動器**。 如果 `<activity>` 元素未使用 icon 指定圖標,則系統將使用 `<application> `元素中的圖標。
**這兩個元素必須配對使用,Activity 才會顯示在應用啟動器中。**
第二個 Activity ShareActivity 旨在便于共享文本和媒體內容。 盡管用戶可以通過從 MainActivity 導航進入此 Activity,但**也可以從發出隱式 Intent(與兩個 Intent 過濾器之一匹配)的另一應用中直接進入 ShareActivity**。
> 注:MIME 類型 `application/vnd.google.panorama360+jpg` 是一個指定全景照片的特殊數據類型,您可以使用[ Google panorama ](https://developer.android.com/reference/com/google/android/gms/panorama/package-summary.html)API 對其進行處理。
### 使用待定 Intent
[PendingIntent](https://developer.android.com/reference/android/app/PendingIntent.html) 對象是 Intent 對象的包裝器。**PendingIntent 的主要目的是授權外部應用使用包含的 Intent,就像是它從您應用本身的進程中執行的一樣。**
待定 Intent 的主要用例包括:
* 聲明用戶使用您的[通知](https://developer.android.com/guide/topics/ui/notifiers/notifications.html)執行操作時所要執行的 Intent(Android 系統的 [NotificationManager](https://developer.android.com/reference/android/app/NotificationManager.html) 執行 Intent)。
* 聲明用戶使用您的 [應用小部件](https://developer.android.com/guide/topics/appwidgets/index.html)執行操作時要執行的 Intent(主屏幕應用執行 Intent)。
* 聲明未來某一特定時間要執行的 Intent(Android 系統的 [AlarmManager](https://developer.android.com/reference/android/app/AlarmManager.html) 執行 Intent)。
由于每個 Intent 對象均設計為由特定類型的應用組件(Activity、Service 或 [BroadcastReceiver](https://developer.android.com/reference/android/content/BroadcastReceiver.html))進行處理,因此還必須基于相同的考慮因素創建 PendingIntent。使用待定 Intent 時,應用不會使用調用(如 startActivity())執行該 Intent。相反,通過調用相應的創建器方法創建 PendingIntent 時,您必須聲明所需的組件類型:
* PendingIntent.getActivity(),適用于啟動 Activity 的 Intent。
* PendingIntent.getService(),適用于啟動 Service 的 Intent。
* PendingIntent.getBroadcast(),適用于啟動 BroadcastReceiver 的 Intent。
除非您的應用正在從其他應用中接收待定 Intent,否則上述用于創建 PendingIntent 的方法可能是您所需的唯一 PendingIntent 方法。
每種方法均會提取當前的應用 Context、您要包裝的 Intent 以及一個或多個指定應如何使用該 Intent 的標志(例如,是否可以多次使用該 Intent)。
如需了解有關使用待定 Intent 的詳細信息,請參閱[通知](https://developer.android.com/guide/topics/ui/notifiers/notifications.html)和應用小部件 API 指南等手冊中每個相應用例的相關文檔。
### Intent 解析
當系統收到隱式 Intent 以啟動 Activity 時,它**根據以下三個方面將該 Intent 與 Intent 過濾器進行比較**,搜索該 Intent 的最佳 Activity:
* Intent 操作
* Intent 數據(URI 和數據類型)
* Intent 類別
下文根據如何在應用的清單文件中聲明 Intent 過濾器,描述 Intent 如何與相應的組件匹配。
### 操作測試
要指定接受的 Intent 操作,Intent 過濾器既可以不聲明任何 `<action>` 元素,也可以聲明多個此類元素。例如:
~~~
<intent-filter>
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.VIEW" />
...
</intent-filter>
~~~
要通過此過濾器,您在 Intent 中指定的操作必須與過濾器中列出的某一操作匹配。
如果該過濾器未列出任何操作,則 Intent 沒有任何匹配項,因此所有 Intent 均無法通過測試。 但是,如果 Intent 未指定操作,則會通過測試(只要過濾器至少包含一個操作)。
#### 類別測試
**要指定接受的 Intent 類別, Intent 過濾器既可以不聲明任何 `<category>` 元素,也可以聲明多個此類元素**。 例如:
~~~
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
...
</intent-filter>
~~~
若要使 Intent 通過類別測試,則 Intent 中的每個類別均必須與過濾器中的類別匹配。反之則未必然,**Intent 過濾器聲明的類別可以超出 Intent 中指定的數量,且 Intent 仍會通過測試**。 因此,**不含類別的 Intent 應當始終會通過此測試,無論過濾器中聲明何種類別均是如此**。
>[info] **注**:Android 會自動將 [CATEGORY_DEFAULT](https://developer.android.com/reference/android/content/Intent.html#CATEGORY_DEFAULT) 類別應用于傳遞給 startActivity() 和 startActivityForResult() 的所有隱式 Intent。因此,**如需 Activity 接收隱式 Intent,則必須將 "android.intent.category.DEFAULT" 的類別包括在其 Intent 過濾器中(如上文的 <intent-filter> 示例所示)**。
### 數據測試
**要指定接受的 Intent 數據, Intent 過濾器既可以不聲明任何 `<data>` 元素,也可以聲明多個此類元素**。 例如:
~~~
<intent-filter>
<data android:mimeType="video/mpeg" android:scheme="http" ... />
<data android:mimeType="audio/mpeg" android:scheme="http" ... />
...
</intent-filter>
~~~
**每個 `<data>` 元素均可指定 URI 結構和數據類型(MIME 媒體類型)。 URI 的每個部分均包含單獨的 scheme、host、port 和 path 屬性**:
~~~
<scheme>://<host>:<port>/<path>
~~~
例如:
~~~
content://com.example.project:200/folder/subfolder/etc
~~~
在此 URI 中,架構是 content,主機是 com.example.project,端口是 200,路徑是 folder/subfolder/etc。
**在 `<data> `元素中,上述每個屬性均為可選,但存在線性依賴關系**:
* 如果未指定架構,則會忽略主機。
* 如果未指定主機,則會忽略端口。
* 如果未指定架構和主機,則會忽略路徑。
將 Intent 中的 URI 與過濾器中的 URI 規范進行比較時,它**僅與過濾器中包含的部分 URI 進行比較**。 例如:
* 如果過濾器僅指定架構,則具有該架構的所有 URI 均與該過濾器匹配。
* 如果過濾器指定架構和權限,但未指定路徑,則具有相同架構和權限的所有 URI 都會通過過濾器,無論其路徑如何均是如此。
* 如果過濾器指定架構、權限和路徑,則僅具有相同架構、權限和路徑的 URI 才會通過過濾器。
>[info]**注**:路徑規范可以包含星號通配符 (*),因此僅需部分匹配路徑名即可。
**數據測試會將 Intent 中的 URI 和 MIME 類型與過濾器中指定的 URI 和 MIME 類型進行比較**。 規則如下:
1. 僅當過濾器未指定任何 URI 或 MIME 類型時,不含 URI 和 MIME 類型的 Intent 才會通過測試。
2. 對于包含 URI 但不含 MIME 類型(既未顯式聲明,也無法通過 URI 推斷得出)的 Intent,僅當其 URI 與過濾器的 URI 格式匹配、且過濾器同樣未指定 MIME 類型時,才會通過測試。
3. 僅當過濾器列出相同的 MIME 類型且未指定 URI 格式時,包含 MIME 類型、但不含 URI 的 Intent 才會通過測試。
4. 僅當 MIME 類型與過濾器中列出的類型匹配時,同時包含 URI 類型和 MIME 類型(通過顯式聲明,或可以通過 URI 推斷得出)的 Intent 才會通過測試的 MIME 類型部分。 如果 Intent 的 URI 與過濾器中的 URI 匹配,或者如果 Intent 具有 content: 或 file: URI 且過濾器未指定 URI,則 Intent 會通過測試的 URI 部分。 換言之,如果過濾器只是列出 MIME 類型,則假定組件支持 content: 和 file: 數據。
最后一條規則,即規則 (d),反映了**期望組件能夠從文件中或內容提供程序獲得本地數據**。因此,其過濾器可以僅列出數據類型,而不必顯式命名 content: 和 file: 架構。這是一個典型的案例。 例如,下文中的 `<data>` 元素向 Android 指出,組件可從內容提供商處獲得并顯示圖像數據:
~~~
<intent-filter>
<data android:mimeType="image/*" />
...
</intent-filter>
~~~
由于大部分可用數據均由內容提供商分發,因此指定數據類型(而非 URI)的過濾器也許最為常見。
另一常見的配置是具有架構和數據類型的過濾器。例如,下文中的 `<data>` 元素向 Android 指出,組件可從網絡中檢索視頻數據以執行操作:
~~~
<intent-filter>
<data android:scheme="http" android:type="video/*" />
...
</intent-filter>
~~~
### Intent 匹配
通過 Intent 過濾器匹配 Intent,這不僅**有助于發現要激活的目標組件,還有助于發現設備上組件集的相關信息**。**Activity,Service和Broadcast Receiver是通過一種叫做Intent的消息來激活的** 。例如,主頁應用通過使用指定 [ACTION_MAIN](https://developer.android.com/reference/android/content/Intent.html#ACTION_MAIN) 操作和 [CATEGORY_LAUNCHER](https://developer.android.com/reference/android/content/Intent.html#CATEGORY_LAUNCHER) 類別的 Intent 過濾器查找所有 Activity,以此填充應用啟動器。
您的應用可以采用類似的方式使用 Intent 匹配。[PackageManager](https://developer.android.com/reference/android/content/pm/PackageManager.html) 提供了一整套 query...() 方法來返回所有能夠接受特定 Intent 的組件。此外,它還提供了一系列類似的 resolve...() 方法來確定響應 Intent 的最佳組件。
- **queryIntentActivities()** 將返回能夠執行那些作為參數傳遞的 Intent 的所有 Activity 列表,返回的是成功匹配的Activity信息。
- **queryIntentServices()** 則可返回類似的服務列表。
- 上面的兩種方法均不會激活組件,而只是列出能夠響應的組件。
- 對于廣播接收器,有一種類似的方法: **queryBroadcastReceivers()**。
### 總結
- 只有一個Intent同時匹配action類別、category類別和data類別才算完全匹配,只有完全匹配才能成功啟動目標Activity。
- action匹配規則
- 只要Intent中的action能夠和過濾規則(`<intent-filter>`)中的任何一個action相同即可匹配成功,action匹配區分大小寫。
- action的匹配要求Intent中的action存在且必須和過濾規則中的其中一個action相同,要求Intent中必須有一個action且必須和過濾規則中的某個action相同。
- category匹配規則
- Intent中如果有category那么所有的category都必須和過濾規則中的其中一個category相同,**如果沒有category的話那么就是默認的category**,即`android.intent.category.DEFAULT` ,所以**為了Activity能夠接收隱式調用,配置多個category的時候必須加上默認的category。**
- data匹配規則
- 主要由mimeType 和URI 組成,其中mimeType代表媒體類型,而URI的結構也復雜,大致如下:
- `<scheme>://<host>:<port>/[<path>]|[<pathPrefix>]|[pathPattern`
- scheme、host、port 分別表示URI的模式、主機名和端口號,其中如果scheme或者host未指定那么URI就無效。
- path、pathPattern、pathPrefix 都是表示路徑信息,其中path表示完整的路徑信息,pathPrefix表示路徑的前綴信息;pathPattern表示完整的路徑,但是它里面包含了通配符(*)。
- data匹配規則:Intent中必須含有data數據,并且data數據能夠完全匹配過濾規則中的某一個data。URI有默認的scheme
- 完全匹配:指的是過濾規則中的data部分也出現在了Intent中的data
- 如果過濾規則中的mimeType指定為image/* 或者text/* 等這種類型的話,那么即使過濾規則中沒有指定URI,URI有默認的scheme是content和file!如果過濾規則中指定了scheme的話那就不是默認的scheme了。
- 前言
- Android系統的體系結構
- Dalvik VM 和 JVM 的比較
- Android 打包應用程序并安裝的過程
- Android ADB工具
- Android應用開發
- Android UI相關知識總結
- Android 中window 、view、 Activity的關系
- Android應用界面
- Android中的drawable和bitmap
- AndroidUI組件adapterView及其子類和Adapter的關系
- Android四大組件
- Android 數據存儲
- SharedPreference
- Android應用的資源
- 數組資源
- 使用Drawable資源
- Material Design
- Android 進程和線程
- 進程
- 線程
- Android Application類的介紹
- 意圖(Intent)
- Intent 和 Intent 過濾器(Google官網介紹)
- Android中關于任務棧的總結
- 任務和返回棧(官網譯文)
- 總結
- Android應用安全現狀與解決方案
- Android 安全開發
- HTTPS
- 安卓 代碼混淆與打包
- 動態注入技術(hook技術)
- 一、什么是hook技術
- 二、常用的Hook 工具
- Xposed源碼剖析——概述
- Xposed源碼剖析——app_process作用詳解
- Xposed源碼剖析——Xposed初始化
- Xposed源碼剖析——hook具體實現
- 無需Root也能Hook?——Depoxsed框架演示
- 三、HookAndroid應用
- 四、Hook原生應用程序
- 五、Hook 檢測/修復
- Android 應用的逆向與加固保護技術
- OpenCV在Android中的開發
- Android高級開發進階
- 高級UI
- UI繪制流程及原理
- Android新布局ConstraintLayout約束布局
- 關鍵幀動畫
- 幀動畫共享元素變換
- Android異步消息處理機制完全解析,帶你從源碼的角度徹底理解
- Android中為什么主線程不會因為Looper.loop()里的死循環卡死?
- 為什么 Android 要采用 Binder 作為 IPC 機制?
- JVM 中一個線程的 Java 棧和寄存器中分別放的是什么?
- Android源碼的Binder權限是如何控制?
- 如何詳解 Activity 的生命周期?
- 為什么Android的Handler采用管道而不使用Binder?
- ThreadLocal,你真的懂了嗎?
- Android屏幕刷新機制