Intent
Intent介紹
Intent是Android應用組件之間通信的消息對象,它通常表明了兩個通信組件的身份以及通信的內容。它最基礎的3個用法如下:
-
Starting an activity
Activity表示應用程序中的單個界面。您可以通過
startActivity(Intent intent)
來啟動Activity的新實例。Intent描述要啟動的Activity以及要傳遞的數據。如果要在Activity結束后收到返回結果,請調用
startActivityForResult(Intent intent)
。您的Activity能在onActivityResult()
回調中的Intent對象中接收到返回結果。
-
Starting a service
Service是一個沒有用戶界面且在后臺執行操作的應用組件。您可以通過
startService(Intent intent)
啟動Service來執行一次性操作(例如下載文件)。如果是Android 5.0(21)及以上可以用JobScheduler
來完成啟動Service的操作。Intent描述要啟動的Service以及要傳遞的數據。如果Service是使用客戶端-服務器接口設計的,則可以通過
bindService(Intent intent)
來啟動綁定到另一個組件的服務。 -
Delivering a broadcast
Broadcast是任何應用可以接收的消息。系統為系統事件提供各種Broadcast,例如系統啟動或設備開始充電時。可以通過
sendBroadcast(Intent intent)或sendOrderedBroadcast(Intent intent)
向其他應用發送Broadcast。
Intent對象
Intent 對象攜帶了 Android 系統用來確定要啟動哪個組件的信息(例如,準確的組件名稱或應當接收該 Intent 的組件類別),以及收件人組件為了正確執行操作而使用的信息等(例如,要采取的操作以及要處理的數據)。
-
Component name
要啟動的組件名稱。
這是可選項,但也是構建顯式Intent的一項重要信息,這意味著Intent應當僅傳遞給由組件名稱定義的應用組件。 如果沒有組件名稱,則Intent是隱式的,且系統將根據其他Intent信息(例如,以下所述的操作、數據和類別)決定哪個組件應當接收Intent。因此,如需在應用中啟動特定的組件,則應指定該組件的名稱。
注: 啟動 Service 時,您應始終指定組件名稱。 否則,您無法確定哪項服務會響應 Intent,且用戶無法看到哪項服務已啟動。
Intent 的這一字段是一個ComponentName對象,您可以使用目標組件的完全限定類名指定此對象,其中包括應用的軟件包名稱。 例如,com.example.ExampleActivity。您可以使用setComponent()、setClass()、setClassName() 或 Intent 構造函數設置組件名稱。
-
action
指定要執行的通用操作(例如,“查看”或“選取”)的字符串。
對于廣播 Intent,這是指已發生且正在報告的操作。操作在很大程度上決定了其余Inten的構成,特別是數據和 extra 中包含的內容。
您可以指定自己的操作,供Intent在您的應用內使用(或者供其他應用在您的應用中調用組件)。但是,您通常應該使用由 Intent 類或其他框架類定義的操作常量。以下是一些用于啟動Activity的常見操作:
-
ACTION_VIEW
如果您擁有一些某項 Activity可向用戶顯示的信息(例如,要使用圖庫應用查看的照片;或者要使用地圖應用查看的地址),請使用 Intent 將此操作與 startActivity() 結合使用。
-
ACTION_SEND
這也稱為“共享”Intent。如果您擁有一些用戶可通過其他應用(例如,電子郵件應用或社交共享應用)共享的數據,則應使用 Intent 將此操作與 startActivity() 結合使用。
您可以使用 setAction() 或 Intent 構造函數為 Intent 指定操作。
如果定義自己的操作,請確保將應用的軟件包名稱作為前綴。 例如:
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
-
-
data
引用待操作數據和/或該數據MIME類型的URI(Uri對象)。提供的數據類型通常由Intent的操作決定。例如,如果操作是 ACTION_EDIT,則數據應包含待編輯文檔的 URI。
創建 Intent 時,除了指定URI以外,指定數據類型(其MIME類型)往往也很重要。例如,能夠顯示圖像的 Activity 可能無法播放音頻文件,即便URI格式十分類似時也是如此。因此,指定數據的MIME類型有助于 Android 系統找到接收Intent的最佳組件。但有時,MIME類型可以從URI中推斷得出,特別當數據是 content: URI 時尤其如此。這表明數據位于設備中,且由ContentProvider控制,這使得數據MIME類型對系統可見。
要僅設置數據 URI,請調用 setData()。 要僅設置MIME類型,請調用setType()。如有必要,您可以使用 setDataAndType() 同時顯式設置二者。
注意:若要同時設置URI和MIME類型,請勿調用setData()和setType(),因為它們會互相抵消彼此的值。請始終使用 setDataAndType() 同時設置 URI 和 MIME 類型。
-
Category
一個包含應處理 Intent 組件類型的附加信息的字符串。 您可以將任意數量的類別描述放入一個 Intent 中,但大多數 Intent 均不需要類別。 以下是一些常見類別:
-
CATEGORY_BROWSABLE
目標 Activity 允許本身通過網絡瀏覽器啟動,以顯示鏈接引用的數據,如圖像或電子郵件。
-
CATEGORY_LAUNCHER
該 Activity 是任務的初始 Activity,在系統的應用啟動器中列出。
您可以使用 addCategory() 指定類別。
-
-
Extra
攜帶完成請求操作所需的附加信息的鍵值對。正如某些操作使用特定類型的數據 URI 一樣,有些操作也使用特定的 extra。
您可以使用各種 putExtra() 方法添加 extra 數據,每種方法均接受兩個參數:鍵名和值。您還可以創建一個包含所有 extra 數據的 Bundle 對象,然后使用 putExtras() 將Bundle 插入 Intent 中。
例如,使用 ACTION_SEND 創建用于發送電子郵件的 Intent 時,可以使用 EXTRA_EMAIL 鍵指定“目標”收件人,并使用 EXTRA_SUBJECT 鍵指定“主題”。
Intent 類將為標準化的數據類型指定多個 EXTRA_* 常量。如需聲明自己的 extra 鍵(對于應用接收的 Intent),請確保將應用的軟件包名稱作為前綴。 例如:
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
-
flag
在 Intent 類中定義的、充當 Intent 元數據的標志。 標志可以指示 Android 系統如何啟動 Activity(例如,Activity 應屬于哪個任務),以及啟動之后如何處理(例如,它是否屬于最近的 Activity 列表)。
Intent類型
Explicit intents(明確的意圖;顯式意圖)
Explicit intents 通過名稱(完全限定類名稱)來指定要啟動的組件。在應用中,一般我們都會通過Explicit intents來啟動組件,因為一般我們都知道要啟動的Activity或者Service的名稱。
當通過顯式意圖啟動Activity或Service時,系統將立即啟動Intent中指定的應用程序組件。
構建顯式Intent
為了構建顯示Intent,Intent對象的Component name屬性必須定義,所有其它屬性都是可選的。
// 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);
Implicit intents(含蓄的意圖;隱式意圖)
Implicit intents 通過描述要啟動組件的特點(如action,data等)來匹配相對應的組件,我們可以通過 Implicit intents訪問其他應用程序或者系統聲明的組件。
當通過隱式意圖啟動Activity或Service時,Android系統通過將Intent的內容與在設備上包括其他應用的清單文件中聲明的 Intent過濾器進行比較,從而找到要啟動的相應組件。如果Intent與Intent過濾器匹配,則系統將啟動該組件,并向其傳遞Intent對象。如果多個Intent過濾器兼容,則系統會顯示一個對話框,支持用戶選取要使用的應用。
下圖展示了,系統如何通過隱式意圖啟動另外一個Activity。
- Activity A 創建一個攜帶action等描述信息的Intent,并傳遞給
startActivity()
- Android 系統搜索所有應用的Intent filters去匹配Intent,當被匹配的Activity B被找到時。
- Android 系統將會調用Activity B的
onCreate()
方法啟動Activity B,并通過它傳遞Intent消息。
注意:為了確保應用的安全性,啟動 Service 時,請使用顯式 Intent,且不要為服務聲明 Intent 過濾器。使用隱式 Intent啟動服務存在安全隱患,因為您無法確定哪些服務將響應Intent,且用戶無法看到哪些服務已啟動。從Android 5.0(21)開始,如果使用隱式Intent調用bindService()
,系統會引發異常。
隱式Intent的構建
隱式意圖指定可以調用能夠執行該操作的設備上的任何應用程序的操作。也就是說我們可以通過隱式意圖讓設備上的其它應用程序來響應我們的操作。如果有多個應用程序可以響應我們的Intent,則會打開一個選擇框由用戶選擇應用程序來響應我們的操作。但是也有可能由于配置文件或者管理員設置等等,沒有App能響應本次操作,如果發生這種情況,應用程序會崩潰,所以我們需要檢測是否有Activity能夠匹配Intent,調用resolveActivity()
來檢測。
// 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);
}
隱式Intent的應用選擇器
當有多個應用程序響應你的隱式Intent時,用戶可以選擇使用哪個應用程序來響應本次操作,并且可以設置默認的響應應用程序。設置之后不會再顯示選擇框,默認使用用戶選擇的應用程序響應我們的操作。
但是如果希望每次都可以選擇不同的應用程序選擇框來選擇哪個應用程序響應我們的操作,則可以通過createChooser()
明確顯示一個選擇對話框.
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);
}
Intent 匹配
Intent Filters 聲明
Intent Filters是意圖過濾器,可以通過在清單文件中使用 <intent-filter> 元素為每個應用組件聲明一個或多個 Intent 過濾器來聲明應用可以接收哪些隱式Intent。每個 Intent 過濾器均根據 Intent 的操作、數據和類別指定自身接受的 Intent 類型。 僅當隱式 Intent 可以通過 Intent 過濾器之一傳遞時,系統才會將該 Intent 傳遞給應用組件。(顯式 Intent 始終會傳遞給其目標,無論組件聲明的 Intent 過濾器如何均是如此。)
每個 Intent 過濾器均由應用清單文件中的 <intent-filter>
元素定義,并嵌套在相應的應用組件(例如,<activity>
元素)中。 在 <intent-filter>
內部,您可以使用以下三個元素中的一個或多個指定要接受的 Intent 類型:
-
<action>
在 name 屬性中,聲明接受的 Intent 操作。該值必須是操作的文本字符串值,而不是類常量。
-
<data>
使用一個或多個指定數據 URI 各個方面(scheme、host、port、path 等)和 MIME 類型的屬性,聲明接受的數據類型。
-
<category>
在 name 屬性中,聲明接受的 Intent 類別。該值必須是操作的文本字符串值,而不是類常量。
注: 為了接收隱式 Intent,必須將
CATEGORY_DEFAULT
類別包括在 Intent 過濾器中。 方法startActivity()
和startActivityForResult()
將按照已申明CATEGORY_DEFAULT
類別的方式處理所有 Intent。 如果未在 Intent 過濾器中聲明此類別,則隱式 Intent 不會解析為您的 Activity。
<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>
元素的過濾器。創建時,必須確定組件能夠處理這些過濾器元素的所有組合即可。
<activity android:name="ShareActivity">
<!-- 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>
聲明多個Intent Filters
如需僅以操作、數據和類別類型的特定組合來處理多種 Intent,則需創建多個 Intent 過濾器。只需要匹配其中任何一個Intent Filters即可。
<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,您必須在清單文件中聲明 Intent 過濾器。但是,廣播接收器的過濾器可以通過調用 registerReceiver()
動態注冊。 稍后,您可以使用 unregisterReceiver()
注銷該接收器。這樣一來,應用便可僅在應用運行時的某一指定時間段內偵聽特定的廣播。
Intent Filters 解析
解析包含多個Intent Filters
如果應用組件的聲明中包含多個 Intent Filters,只需要匹配其中任何一個即可。
解析包含單個Intent Filters
當系統收到隱式 Intent 以啟動 Activity 時,它根據以下三個方面將該 Intent 與 Intent 過濾器進行比較,(必須每一個條件都匹配)搜索該 Intent 的最佳 Activity:
-
Action.
要指定接受的 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 未指定操作,則會通過測試(只要過濾器至少包含一個操作)。
Category.
要指定接受的 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 應當始終會通過此測試,無論過濾器中聲明何種類別均是如此。
注: Android 會自動將 CATEGORY_DEFAULT
類別應用于傳遞給 startActivity()
和 startActivityForResult()
的所有隱式 Intent。因此,如需 Activity 接收隱式 Intent,則必須將 "android.intent.category.DEFAULT"
的類別包括在其 Intent 過濾器中(如上文的 <intent-filter>
示例所示)。
- Data (both URI and data type).
要指定接受的 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 才會通過過濾器。
注:路徑規范可以包含星號通配符 (*),因此僅需部分匹配路徑名即可。
數據測試會將 Intent 中的 URI 和 MIME 類型與過濾器中指定的 URI 和 MIME 類型進行比較。 規則如下:
僅當過濾器未指定任何 URI 或 MIME 類型時,不含 URI 和 MIME 類型的 Intent 才會通過測試。
對于包含 URI 但不含 MIME 類型(既未顯式聲明,也無法通過 URI 推斷得出)的 Intent,僅當其 URI 與過濾器的 URI 格式匹配、且過濾器同樣未指定 MIME 類型時,才會通過測試。
僅當過濾器列出相同的 MIME 類型且未指定 URI 格式時,包含 MIME 類型、但不含 URI 的 Intent 才會通過測試。
僅當 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 Filters 查詢
通過 Intent 過濾器匹配 Intent,這不僅有助于發現要激活的目標組件,還有助于發現設備上組件集的相關信息。 例如,主頁應用通過使用指定 ACTION_MAIN 操作和 CATEGORY_LAUNCHER 類別的 Intent 過濾器查找所有 Activity,以此填充應用啟動器。
您的應用可以采用類似的方式使用 Intent 匹配。PackageManager 提供了一整套 query...() 方法來返回所有能夠接受特定 Intent 的組件。此外,它還提供了一系列類似的 resolve...() 方法來確定響應 Intent 的最佳組件。 例如,queryIntentActivities() 將返回能夠執行那些作為參數傳遞的 Intent 的所有 Activity 列表,而 queryIntentServices() 則可返回類似的服務列表。這兩種方法均不會激活組件,而只是列出能夠響應的組件。 對于廣播接收器,有一種類似的方法: queryBroadcastReceivers()。