1、Intent的用途
Intent主要有如下幾種重要用途:
- 1. 啓動Activity:能夠將Intent對象傳遞給startActivity()方法或startActivityForResult()方法以啓動一個Activity,該Intent對象包含了要啓動的Activity的信息及其餘必要的數據。
- 2. 啓動Service:能夠將Intent對象傳遞給startService()方法或bindService()方法以啓動一個Service,該Intent對象包含了要啓動的Service的信息及其餘必要的數據。關於使用startService()方法啓動Service,能夠參見《Android中startService基本使用方法概述》。關於使用bindService()方法啓動Service,能夠參見《Android中bindService基本使用方法概述》。
- 3. 發送廣播:廣播是一種全部App均可以接收的信息。Android系統會發布各類類型的廣播,好比發佈開機廣播或手機充電廣播等。咱們也能夠給其餘的App發送廣播,能夠將Intent對象傳遞給sendBroadcast()方法或sendOrderedBroadcast()方法或sendStickyBroadcast()方法以發送自定義廣播。
2、Intent的類型
有兩種類型的Intent:
explicit(顯式)的和implict(隱式)的。
- 顯式的Intent:若是Intent中明確包含了要啓動的組件的完整類名(包名及類名),那麼這個Intent就是explict的,即顯式的。使用顯式Intent最典型的情形是在你本身的App中啓動一個組件,由於你本身確定知道本身的要啓動的組件的類名。好比,爲了響應用戶操做經過顯式的Intent在你的App中啓動一個Activity或啓動一個Service下載文件。
當建立了一個顯式Intent去啓動Activity或Service的時候,系統會當即啓動Intent中所指定的組件。 android
- 隱式的Intent:若是Intent沒有包含要啓動的組件的完整類名,那麼這個Intent就是implict的,即隱式的。雖然隱式的Intent沒有指定要啓動的組件的類名,可是通常狀況下,隱式的Intent都要指定須要執行的action。通常,隱式的Intent只用在當咱們想在本身的App中經過Intent啓動另外一個App的組件的時候,讓另外一個App的組件接收並處理該Intent。例如,你想在地圖上給用戶顯示一個位置,可是你的App又不支持地圖展現,這時候你能夠將位置信息放入到一個Intent中,而後給它指定相應的action,經過這樣隱式的Intent請求其餘的地圖型的App(例如Google Map、百度地圖等)來在地圖中展現一個指定的位置。隱式的Intent也體現了Android的一種設計哲學:我本身的App無需一應俱全全部功能,能夠經過與其餘App組合起來,給用戶提供很好的用戶體驗。而鏈接本身的App與其餘App的紐帶就是隱式Intent。
當建立了一個隱式Intent去使用的時候,Android系統會將該隱式Intent所包含的信息與設備上其餘全部App中manifest文件中註冊的組件的Intent Filters進行對比過濾,從中找出知足可以接收處理該隱式Intent的App和對應的組件。若是有多個App中的某個組件都符合條件,那麼Android會彈出一個對話框讓用戶選擇須要啓動哪一個App。編程
Intent Filter,即Intent過濾器,
一個組件能夠包含0個或多個Intent Filter。Intent Filter是寫在App的manifest文件中的,
其經過設置action或uri數據類型等指明瞭組件可以處理接收的Intent的類型。
若是你給你的Activity設置了Intent Filter,那麼這就使得其餘的App有可能經過隱式Intent啓動你的這個Activity。反之,若是你的Activity不包含任何Intent Filter,那麼該Activity只能經過顯式Intent啓動,因爲咱們通常不會暴露出咱們組件的完整類名,因此這種狀況下,其餘的App基本就不可能經過Intent啓動咱們的Activity了(由於他們不知道該Activity的完整類名),只能由咱們本身的App經過顯式Intent啓動。
須要注意的是,
爲了確保App的安全性,咱們應該老是使用顯式Intent去啓動Service而且不要爲該Service設置任何的Intent Filter。經過隱式的Intent啓動Service是有風險的,由於你不肯定最終哪一個App中的哪一個Service會啓動起來以響應你的隱式Intent,更悲催的是,因爲Service沒有UI的在後臺運行,因此用戶也不知道哪一個Service運行了。從Android 5.0 (API level 21)開始,用隱式Intent調用bindService()方法,Android會拋出異常,可是也有相應技巧,將一個隱式的Intent轉換爲顯式的Intent,而後用顯式的Intent去調用bindService()方法就沒有問題了,具體解決辦法能夠參見博文《Android中經過Messenger與Service實現進程間雙向通訊》中最後的「注意事項」部分,裏面有相關代碼的解決方案。
3、Intent的組成
Android能夠根據Intent所攜帶的信息去查找要啓動的組件,Intent還攜帶了一些數據信息以便要啓動的組件根據Intent中的這些數據作相應的處理。
Intent由6部分信息組成:Component Name、Action、Data、Category、Extras、Flags。根據信息的做用用於,又可分爲三類:
- a. Component Name、Action、Data、Category爲一類,這4中信息決定了Android會啓動哪一個組件,其中Component Name用於在顯式Intent中使用,Action、Data、Category、Extras、Flags用於在隱式Intent中使用。
- b. Extras爲一類,裏面包含了具體的用於組件實際處理的數據信息。
- c. Flags爲一類,其是Intent的元數據,決定了Android對其操做的一些行爲,下面會介紹。
三A、Component name
Component name 要啓動的組件的名稱。若是你想使用顯式的Intent,那麼你就必須指定該參數,一旦設置了component name,Android會直接將Intent傳遞給組件名所指定的組件去啓動它。若是沒有設置component name,那麼該Intent就是隱式的,Android系統會根據其餘的Intent的信息(例以下面要介紹到的action、data、category等)作一些比較判斷決定最終要啓動哪一個組件。因此,若是你啓動一個你本身App中的組件,你應該經過指定component name經過顯式Intent去啓動它(由於你知道該組件的完整類名)。
須要注意的是,當啓動Service的時候,你應該老是指定Component Name。不然,你不肯定最終哪一個App的哪一個組件被啓動了,而且用戶也看不到哪一個Service啓動了。
component name在Intent中對應的
field是ComponentName對象,你能夠經過要啓動的組件的完整類名(包括應用的包名)指定該值,例如
com.example.ExampleActivity。你能夠經過Intent的setComponent()方法、setClass()方法、setClassName()方法或Intent的構造函數指定component name。
三B、Action
是表示了要執行操做的字符串,好比查看或選擇,
其對應着Intent Filter中的action標籤<action />。
你能夠指定你獨有的action以便於你的App中的Intent的使用或其餘App中經過Intent調用你的App中的組件。Intent類和Android中其餘framework級別的一些類也提供了許多已經定義好的具備必定通用意義的action。如下是一些用於啓動Activity的常見的action:
Intent.ACTION_VIEW 其值爲 「android.intent.action.VIEW」,當你有一些信息想讓經過其餘Activity展現給用戶的時候,你就能夠將Intent的action指定爲ACTION_VIEW,好比在一個圖片應用中查看一張圖片,或者在一個地圖應用中展示一個位置。
Intent.ACTION_SEND 其值爲」android.intent.action.SEND」,該action經常使用來作「分享」使用,當你有一些數據想經過其餘的App(例如QQ、微信、百度雲等)分享出去的時候,就可使用此action構建Intent對象,並將其傳遞給startActivity()方法,因爲手機上可能有多個App的Activity均支持ACTION_SEND這一action,因此頗有可能會出現以下的圖片所示的情形讓用戶具體選擇要經過哪一個App分享你的數據:
能夠經過查看Intent類瞭解更多的Intent預約義的一些常見的action。Android中framework級別的一些類也定義了一些action,例如Settings中定義了一些action用以分別打開系統中「設置」這個應用的不一樣界面以完成對指定配置(如WLAN設置、語言設置等)。
你能夠經過調用intent對象的setAction()方法或在Intent的構造函數中指定intent的action。
若是你定義了你本身的action,請務必將你的App的包名做爲該action的前綴,這是一種良好的編程習慣,避免形成混淆,例如:
複製代碼 代碼以下:
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
Data
此處所說的Intent中的data指的是Uri對象和數據的MIME類型,其對應着Intent Filter中的data標籤<data />。
一個完整的Uri由scheme、host、port、path組成,格式是<scheme>://<host>:<port>/<path>,例如content://com.example.project:200/folder/subfolder/etc。Uri就像一個數據連接,組件能夠根據此Uri得到最終的數據來源。一般將Uri和action結合使用,好比咱們將action設置爲ACTION_VIEW,咱們應該提供將要被編輯修改的文檔的Uri。
當建立了一個Intent對象的時候,除了指定Uri以外,指定數據的MIME類型也很重要。例如,一個Activity可以顯示圖片,可是不可以播放視頻,顯示圖片的Uri和播放視頻的Uri可能很相似,爲了避免讓Android誤將一個含有視頻Uri的Intent對象傳遞給一個只能顯示圖片的Activity,咱們須要在該Activity的Intent Filter中指定MIME類型爲圖片(例如<data android:mimeType="image/*" ... />)而且還要給Intent對象設置對應的圖片類型的MIME,這樣Android就會基於Uri和MIME類型將Intent傳遞給符合條件的組件。而後有個特例,若是Uri使用的是content:協議,那麼這就說明Uri所提供的數據未來自於本地設備,即數據由ContentProvider提供,這種狀況下Android會根據Uri自動推斷出MIME類型,此種狀況咱們無需再本身指定MIME類型。
若是隻設置數據的Uri,須要調用Intent對象的setData()方法;若是隻設置數據的MIME類型,須要調用Intent對象的setType()方法;若是要同時設置數據的Uri和MIME類型,須要調用Intent對象的setDataAndType()方法。
須要注意的是,若是你想要同時設置數據的Uri和MIME類型,不要前後調用Intent對象的setData()方法和setType()方法,由於setData()方法和setType()是互斥的,即若是調用了setData()方法,會將Intent中已經經過setType()方法設置的MIME類型重置爲空。若是調用了setType()方法,會將Intent中已經經過setData()方法設置的Uri重置爲空。因此在須要同時設置數據的Uri和MIME類型的時候,必定要調用Intent對象的setDataAndType()方法,而不是分別調用setData()方法和setType()方法。
Category
category包含了關於組件如何處理Intent的一些其餘信息,雖然能夠在Intent中加入任意數量的category,可是大多數的Intent其實不須要category。
如下是一些常見的category:
CATEGORY_BROWSABLE 目標組件會容許本身經過一個連接被一個Web瀏覽器啓動,該連接多是一個圖片連接或e-mail信息等。
CATEGORY_LAUNCHER 用於標識Activity是某個App的入口Activity。
你能夠在Intent類中查找到更多預約義的category。
Extras extras,顧名思義,就是額外的數據信息,Intent中有一個Bundle對象存儲着各類鍵值對,接收該Intent的組件能夠從中讀取出所須要的信息以便完成相應的工做。有的Intent須要靠Uri攜帶數據,有的Intent是靠extras攜帶數據信息。
你能夠經過調用Intent對象的各類重載的putExtra(key, value)方法向Intent中加入各類鍵值對形式的額外數據。你也能夠直接建立一個Bundle對象,向該Bundle對象傳入不少鍵值對,而後經過調用Intent對象的putExtras(Bundle)方法將其一塊設置給Intent對象中去。
例如,你建立了一個action爲ACTION_SEND的Intent對象,而後想用它啓動e-mail發送郵件,那麼你須要給該Intent對象設置兩個extra的值:
用Intent.EXTRA_EMAIL 做爲key值設置收件方,用Intent.EXTRA_SUBJECT 做爲key值設置郵件標題。
Intent類裏面也指定了不少預約義的EXTRA_*形式的extra,例如上面咱們提到的(Intent.EXTRA_EMAIL 和Intent.EXTRA_SUBJECT)。若是你想要聲明你本身自定義的extra,請確保將你的App的包名做爲你的extra的前綴,例如:
複製代碼 代碼以下:
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
Flags
flag就是標記的意思,Intent類中定義的flag可以起到做爲Intent對象的元數據的做用。這些flag會告知Android系統如何啓動Activity(例如,新啓動的Activity屬於哪一個task)以及在該Activity啓動後如何對待它(好比)。更多信息可參見Intent的setFlags()方法。
一、顯式Intent使用示例
1
2
|
Intent intent = new Intent(this, ActivityB.class);
startActivity(intent);
|
上面的代碼在Intent的構造函數中指定了要啓動的組件的ComponentName是ActivityB,該intent對象是顯式的,調用startActivity(intent)時,Android系統會當即啓動ActivityB。
二、隱式Intent使用示例
以前提到過,在使用隱式Intent的時候須要指定其action。若是你的App不能完成某個功能,可是其餘的App可能完成該功能,那麼你就能夠用隱式Intent啓動其餘的App去完成相應的功能。例如,你有一段文本信息,想經過其餘App分享出去,那麼隱式Intent對象去啓動潛在的支持分享的App,示例代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
Intent sendIntent = new Intent();
// 設置action, action對隱式Intent來講是很是重要的
sendIntent.setAction(Intent.ACTION_SEND);
// 設置數據的MIME類型爲純文本類型
sendIntent.setType("text/plain");
// 設置額外的數據
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
// 獲取包管理器
PackageManager pm = getPackageManager();
// 先判斷系統中有沒有潛在的App的Activity支持對該sendIntent的接收與處理
if (pm.resolveActivity(sendIntent, 0) != null) {
startActivity(sendIntent);
}
|
上面的代碼中,咱們構建了一個Intent對象,並無給其設置component name,因此該Intent是一個隱式的Intent對象。咱們首先給intent設置了action的值爲Intent.ACTION_SEND,action對隱式Intent來講是很是重要的。而後咱們將intent的數據的MIME類型設置爲純文本類型(「text/plain」),告知Android咱們的Intent持有的是文本類型的數據。最後咱們將實際的文本數據經過putExtra()方法做爲額外數據設置進去。
須要注意的是,在構建好了Intent對象以後,咱們沒有當即執行startActivity(sendIntent)方法,而是將sendIntent做爲參數傳遞給了PackageManager的resolveActivity()方法中,該方法會讓Android根據該sendIntent找到潛在的適合啓動的組件的信息,並以ResolveInfo類的對象的形式返回結果,若是返回null,表示當前系統中沒有任何組件能夠接收並處理該sendIntent。若是返回不是null,就代表系統中至少存在一個組件能夠接收並處理該sendIntent,只有在這種狀況下,咱們纔會執行代碼startActivity(sendIntent),在經過intent啓動組件以前先判斷要啓動的組件存不存在是個良好的編程習慣,由於若是系統中不存在支持你的intent的組件,那麼當你調用startActivity()、startService()、bindService()等方法的時候,Android就會拋出異常。
4、強制用戶使用App Chooser
在上文中咱們已經提到,若是咱們的Intent是隱式的,當咱們經過startActivity(intent)嘗試啓動組件的時候,可能Android系統會顯示上面的截圖文件詢問用戶要啓動哪一個App,有時候用戶會將某一個App設置爲默認的App,這樣下次咱們再執行代碼startActivity(intent)的時候就有可能不會再出現選擇App的界面,而是直接運行上次用戶設置爲默認App的應用。這對於用戶選擇一個默認瀏覽器打開網頁這種情形是有好處的,由於通常一個用戶習慣於用一個本身喜歡的瀏覽器。
可是若是用戶不想每次都用同一個默認App處理這樣的情形怎麼辦呢?這時候咱們能夠在代碼中明確地使用App選擇對話框,好比黨咱們的App執行一個action爲ACTION_SEND的分享功能時,咱們想讓用戶分享本身數據的代碼,可是咱們不肯定用戶想經過哪一個App去分享,咱們想每次都彈出App選擇對話框讓用戶決定想經過哪一個App分享,示例代碼以下所示:
1
2
3
4
5
6
7
8
9
10
11
12
|
Intent sendIntent = new Intent(Intent.ACTION_SEND);
...
String title = "請選擇想經過哪一個App分享數據";
// 驗證是否有App可以接收並處理sendIntent
if (sendIntent.resolveActivity(getPackageManager()) != null) {
// 根據sendIntent建立一個須要顯示App選擇對話框的intent對象
Intent chooserIntent = Intent.createChooser(sendIntent, title);
// 咱們使用chooserIntent做爲startActivity()方法的參數,而非sendIntent
startActivity(chooserIntent);
}
|
首先咱們建立了咱們原始的sendIntent,並對其設置action等相關信息,而後咱們將sendIntent傳遞給了Intent.createChooser()方法中,建立了另外一個chooserIntent。後面咱們經過調用Intent.resolveActivity(PackageManager)方法判斷系統中是否有App可以接收並處理sendIntent,該方法與上面以前提到過的PackageManager的resolveActivity()方法是等價的。最後咱們使用chooserIntent做爲startActivity()方法的參數,而非sendIntent,chooserIntent會讓Android系統強制顯示用戶選擇App處理Intent的界面。
本文大部分參考了Android中對Intent部分的Develop Guide的描述,但願本文對你們更好地使用Intent對象有所幫助。