詳解Android中Intent的使用方法
一、Intent的用途
Intent主要有以下幾種重要用途:
1. 啟動Activity:可以將Intent對象傳遞給startActivity()方法或startActivityForResult()方法以啟動一個Activity,該Intent對象包含了要啟動的Activity的信息及其他必要的數(shù)據(jù)。
2. 啟動Service:可以將Intent對象傳遞給startService()方法或bindService()方法以啟動一個Service,該Intent對象包含了要啟動的Service的信息及其他必要的數(shù)據(jù)。關(guān)于使用startService()方法啟動Service,可以參見《Android中startService基本使用方法概述》。關(guān)于使用bindService()方法啟動Service,可以參見《Android中bindService基本使用方法概述》。
3. 發(fā)送廣播:廣播是一種所有App都可以接收的信息。Android系統(tǒng)會發(fā)布各種類型的廣播,比如發(fā)布開機(jī)廣播或手機(jī)充電廣播等。我們也可以給其他的App發(fā)送廣播,可以將Intent對象傳遞給sendBroadcast()方法或sendOrderedBroadcast()方法或sendStickyBroadcast()方法以發(fā)送自定義廣播。
二、Intent的類型
有兩種類型的Intent:explicit(顯式)的和implict(隱式)的。
顯式的Intent:如果Intent中明確包含了要啟動的組件的完整類名(包名及類名),那么這個Intent就是explict的,即顯式的。使用顯式Intent最典型的情形是在你自己的App中啟動一個組件,因?yàn)槟阕约嚎隙ㄖ雷约旱囊獑拥慕M件的類名。比如,為了響應(yīng)用戶操作通過顯式的Intent在你的App中啟動一個Activity或啟動一個Service下載文件。
隱式的Intent:如果Intent沒有包含要啟動的組件的完整類名,那么這個Intent就是implict的,即隱式的。雖然隱式的Intent沒有指定要啟動的組件的類名,但是一般情況下,隱式的Intent都要指定需要執(zhí)行的action。一般,隱式的Intent只用在當(dāng)我們想在自己的App中通過Intent啟動另一個App的組件的時候,讓另一個App的組件接收并處理該Intent。例如,你想在地圖上給用戶顯示一個位置,但是你的App又不支持地圖展示,這時候你可以將位置信息放入到一個Intent中,然后給它指定相應(yīng)的action,通過這樣隱式的Intent請求其他的地圖型的App(例如Google Map、百度地圖等)來在地圖中展示一個指定的位置。隱式的Intent也體現(xiàn)了Android的一種設(shè)計哲學(xué):我自己的App無需包羅萬象所有功能,可以通過與其他App組合起來,給用戶提供很好的用戶體驗(yàn)。而連接自己的App與其他App的紐帶就是隱式Intent。
當(dāng)創(chuàng)建了一個顯式Intent去啟動Activity或Service的時候,系統(tǒng)會立即啟動Intent中所指定的組件。
當(dāng)創(chuàng)建了一個隱式Intent去使用的時候,Android系統(tǒng)會將該隱式Intent所包含的信息與設(shè)備上其他所有App中manifest文件中注冊的組件的Intent Filters進(jìn)行對比過濾,從中找出滿足能夠接收處理該隱式Intent的App和對應(yīng)的組件。如果有多個App中的某個組件都符合條件,那么Android會彈出一個對話框讓用戶選擇需要啟動哪個App。
Intent Filter,即Intent過濾器,一個組件可以包含0個或多個Intent Filter。Intent Filter是寫在App的manifest文件中的,其通過設(shè)置action或uri數(shù)據(jù)類型等指明了組件能夠處理接收的Intent的類型。如果你給你的Activity設(shè)置了Intent Filter,那么這就使得其他的App有可能通過隱式Intent啟動你的這個Activity。反之,如果你的Activity不包含任何Intent Filter,那么該Activity只能通過顯式Intent啟動,由于我們一般不會暴露出我們組件的完整類名,所以這種情況下,其他的App基本就不可能通過Intent啟動我們的Activity了(因?yàn)樗麄儾恢涝揂ctivity的完整類名),只能由我們自己的App通過顯式Intent啟動。
需要注意的是,為了確保App的安全性,我們應(yīng)該總是使用顯式Intent去啟動Service并且不要為該Service設(shè)置任何的Intent Filter。通過隱式的Intent啟動Service是有風(fēng)險的,因?yàn)槟悴淮_定最終哪個App中的哪個Service會啟動起來以響應(yīng)你的隱式Intent,更悲催的是,由于Service沒有UI的在后臺運(yùn)行,所以用戶也不知道哪個Service運(yùn)行了。從Android 5.0 (API level 21)開始,用隱式Intent調(diào)用bindService()方法,Android會拋出異常,但是也有相應(yīng)技巧,將一個隱式的Intent轉(zhuǎn)換為顯式的Intent,然后用顯式的Intent去調(diào)用bindService()方法就沒有問題了,具體解決辦法可以參見博文《Android中通過Messenger與Service實(shí)現(xiàn)進(jìn)程間雙向通信》中最后的“注意事項(xiàng)”部分,里面有相關(guān)代碼的解決方案。
三、Intent的組成
Android可以根據(jù)Intent所攜帶的信息去查找要啟動的組件,Intent還攜帶了一些數(shù)據(jù)信息以便要啟動的組件根據(jù)Intent中的這些數(shù)據(jù)做相應(yīng)的處理。
Intent由6部分信息組成:Component Name、Action、Data、Category、Extras、Flags。根據(jù)信息的作用用于,又可分為三類:
a. Component Name、Action、Data、Category為一類,這4中信息決定了Android會啟動哪個組件,其中Component Name用于在顯式Intent中使用,Action、Data、Category、Extras、Flags用于在隱式Intent中使用。
b. Extras為一類,里面包含了具體的用于組件實(shí)際處理的數(shù)據(jù)信息。
c. Flags為一類,其是Intent的元數(shù)據(jù),決定了Android對其操作的一些行為,下面會介紹。
Component name
要啟動的組件的名稱。如果你想使用顯式的Intent,那么你就必須指定該參數(shù),一旦設(shè)置了component name,Android會直接將Intent傳遞給組件名所指定的組件去啟動它。如果沒有設(shè)置component name,那么該Intent就是隱式的,Android系統(tǒng)會根據(jù)其他的Intent的信息(例如下面要介紹到的action、data、category等)做一些比較判斷決定最終要啟動哪個組件。所以,如果你啟動一個你自己App中的組件,你應(yīng)該通過指定component name通過顯式Intent去啟動它(因?yàn)槟阒涝摻M件的完整類名)。
需要注意的是,當(dāng)啟動Service的時候,你應(yīng)該總是指定Component Name。否則,你不確定最終哪個App的哪個組件被啟動了,并且用戶也看不到哪個Service啟動了。
component name在Intent中對應(yīng)的field是ComponentName對象,你可以通過要啟動的組件的完整類名(包括應(yīng)用的包名)指定該值,例如com.example.ExampleActivity。你可以通過Intent的setComponent()方法、setClass()方法、setClassName()方法或Intent的構(gòu)造函數(shù)指定component name。
Action
是表示了要執(zhí)行操作的字符串,比如查看或選擇,其對應(yīng)著Intent Filter中的action標(biāo)簽<action />。
你可以指定你獨(dú)有的action以便于你的App中的Intent的使用或其他App中通過Intent調(diào)用你的App中的組件。Intent類和Android中其他framework級別的一些類也提供了許多已經(jīng)定義好的具有一定通用意義的action。以下是一些用于啟動Activity的常見的action:
Intent.ACTION_VIEW 其值為 “android.intent.action.VIEW”,當(dāng)你有一些信息想讓通過其他Activity展示給用戶的時候,你就可以將Intent的action指定為ACTION_VIEW,比如在一個圖片應(yīng)用中查看一張圖片,或者在一個地圖應(yīng)用中展現(xiàn)一個位置。
Intent.ACTION_SEND 其值為”android.intent.action.SEND”,該action常用來做“分享”使用,當(dāng)你有一些數(shù)據(jù)想通過其他的App(例如QQ、微信、百度云等)分享出去的時候,就可以使用此action構(gòu)建Intent對象,并將其傳遞給startActivity()方法,由于手機(jī)上可能有多個App的Activity均支持ACTION_SEND這一action,所以很有可能會出現(xiàn)如下的圖片所示的情形讓用戶具體選擇要通過哪個App分享你的數(shù)據(jù):
可以通過查看Intent類了解更多的Intent預(yù)定義的一些常見的action。Android中framework級別的一些類也定義了一些action,例如Settings中定義了一些action用以分別打開系統(tǒng)中“設(shè)置”這個應(yīng)用的不同界面以完成對指定配置(如WLAN設(shè)置、語言設(shè)置等)。
你可以通過調(diào)用intent對象的setAction()方法或在Intent的構(gòu)造函數(shù)中指定intent的action。
如果你定義了你自己的action,請務(wù)必將你的App的包名作為該action的前綴,這是一種良好的編程習(xí)慣,避免造成混淆,例如:
Data
此處所說的Intent中的data指的是Uri對象和數(shù)據(jù)的MIME類型,其對應(yīng)著Intent Filter中的data標(biāo)簽<data />。
一個完整的Uri由scheme、host、port、path組成,格式是<scheme>://<host>:<port>/<path>,例如content://com.example.project:200/folder/subfolder/etc。Uri就像一個數(shù)據(jù)鏈接,組件可以根據(jù)此Uri獲得最終的數(shù)據(jù)來源。通常將Uri和action結(jié)合使用,比如我們將action設(shè)置為ACTION_VIEW,我們應(yīng)該提供將要被編輯修改的文檔的Uri。
當(dāng)創(chuàng)建了一個Intent對象的時候,除了指定Uri之外,指定數(shù)據(jù)的MIME類型也很重要。例如,一個Activity能夠顯示圖片,但是不能夠播放視頻,顯示圖片的Uri和播放視頻的Uri可能很類似,為了不讓Android誤將一個含有視頻Uri的Intent對象傳遞給一個只能顯示圖片的Activity,我們需要在該Activity的Intent Filter中指定MIME類型為圖片(例如<data android:mimeType="image/*" ... />)并且還要給Intent對象設(shè)置對應(yīng)的圖片類型的MIME,這樣Android就會基于Uri和MIME類型將Intent傳遞給符合條件的組件。然后有個特例,如果Uri使用的是content:協(xié)議,那么這就說明Uri所提供的數(shù)據(jù)將來自于本地設(shè)備,即數(shù)據(jù)由ContentProvider提供,這種情況下Android會根據(jù)Uri自動推斷出MIME類型,此種情況我們無需再自己指定MIME類型。
如果只設(shè)置數(shù)據(jù)的Uri,需要調(diào)用Intent對象的setData()方法;如果只設(shè)置數(shù)據(jù)的MIME類型,需要調(diào)用Intent對象的setType()方法;如果要同時設(shè)置數(shù)據(jù)的Uri和MIME類型,需要調(diào)用Intent對象的setDataAndType()方法。
需要注意的是,如果你想要同時設(shè)置數(shù)據(jù)的Uri和MIME類型,不要先后調(diào)用Intent對象的setData()方法和setType()方法,因?yàn)閟etData()方法和setType()是互斥的,即如果調(diào)用了setData()方法,會將Intent中已經(jīng)通過setType()方法設(shè)置的MIME類型重置為空。如果調(diào)用了setType()方法,會將Intent中已經(jīng)通過setData()方法設(shè)置的Uri重置為空。所以在需要同時設(shè)置數(shù)據(jù)的Uri和MIME類型的時候,一定要調(diào)用Intent對象的setDataAndType()方法,而不是分別調(diào)用setData()方法和setType()方法。
Category
category包含了關(guān)于組件如何處理Intent的一些其他信息,雖然可以在Intent中加入任意數(shù)量的category,但是大多數(shù)的Intent其實(shí)不需要category。
以下是一些常見的category:
CATEGORY_BROWSABLE 目標(biāo)組件會允許自己通過一個鏈接被一個Web瀏覽器啟動,該鏈接可能是一個圖片鏈接或e-mail信息等。
CATEGORY_LAUNCHER 用于標(biāo)識Activity是某個App的入口Activity。
你可以在Intent類中查找到更多預(yù)定義的category。
Extras
extras,顧名思義,就是額外的數(shù)據(jù)信息,Intent中有一個Bundle對象存儲著各種鍵值對,接收該Intent的組件可以從中讀取出所需要的信息以便完成相應(yīng)的工作。有的Intent需要靠Uri攜帶數(shù)據(jù),有的Intent是靠extras攜帶數(shù)據(jù)信息。
你可以通過調(diào)用Intent對象的各種重載的putExtra(key, value)方法向Intent中加入各種鍵值對形式的額外數(shù)據(jù)。你也可以直接創(chuàng)建一個Bundle對象,向該Bundle對象傳入很多鍵值對,然后通過調(diào)用Intent對象的putExtras(Bundle)方法將其一塊設(shè)置給Intent對象中去。
例如,你創(chuàng)建了一個action為ACTION_SEND的Intent對象,然后想用它啟動e-mail發(fā)送郵件,那么你需要給該Intent對象設(shè)置兩個extra的值:
用Intent.EXTRA_EMAIL 作為key值設(shè)置收件方,用Intent.EXTRA_SUBJECT 作為key值設(shè)置郵件標(biāo)題。
Intent類里面也指定了很多預(yù)定義的EXTRA_*形式的extra,例如上面我們提到的(Intent.EXTRA_EMAIL 和Intent.EXTRA_SUBJECT)。如果你想要聲明你自己自定義的extra,請確保將你的App的包名作為你的extra的前綴,例如:
Flags
flag就是標(biāo)記的意思,Intent類中定義的flag能夠起到作為Intent對象的元數(shù)據(jù)的作用。這些flag會告知Android系統(tǒng)如何啟動Activity(例如,新啟動的Activity屬于哪個task)以及在該Activity啟動后如何對待它(比如)。更多信息可參見Intent的setFlags()方法。
1、顯式Intent使用示例
Intent intent = new Intent(this, ActivityB.class); startActivity(intent);
上面的代碼在Intent的構(gòu)造函數(shù)中指定了要啟動的組件的ComponentName是ActivityB,該intent對象是顯式的,調(diào)用startActivity(intent)時,Android系統(tǒng)會立即啟動ActivityB。
2、隱式Intent使用示例
之前提到過,在使用隱式Intent的時候需要指定其action。如果你的App不能完成某個功能,但是其他的App可能完成該功能,那么你就可以用隱式Intent啟動其他的App去完成相應(yīng)的功能。例如,你有一段文本信息,想通過其他App分享出去,那么隱式Intent對象去啟動潛在的支持分享的App,示例代碼如下:
Intent sendIntent = new Intent(); // 設(shè)置action, action對隱式Intent來說是非常重要的 sendIntent.setAction(Intent.ACTION_SEND); // 設(shè)置數(shù)據(jù)的MIME類型為純文本類型 sendIntent.setType("text/plain"); // 設(shè)置額外的數(shù)據(jù) sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); // 獲取包管理器 PackageManager pm = getPackageManager(); // 先判斷系統(tǒng)中有沒有潛在的App的Activity支持對該sendIntent的接收與處理 if (pm.resolveActivity(sendIntent, 0) != null) { startActivity(sendIntent); }
上面的代碼中,我們構(gòu)建了一個Intent對象,并沒有給其設(shè)置component name,所以該Intent是一個隱式的Intent對象。我們首先給intent設(shè)置了action的值為Intent.ACTION_SEND,action對隱式Intent來說是非常重要的。然后我們將intent的數(shù)據(jù)的MIME類型設(shè)置為純文本類型(“text/plain”),告知Android我們的Intent持有的是文本類型的數(shù)據(jù)。最后我們將實(shí)際的文本數(shù)據(jù)通過putExtra()方法作為額外數(shù)據(jù)設(shè)置進(jìn)去。
需要注意的是,在構(gòu)建好了Intent對象之后,我們沒有立即執(zhí)行startActivity(sendIntent)方法,而是將sendIntent作為參數(shù)傳遞給了PackageManager的resolveActivity()方法中,該方法會讓Android根據(jù)該sendIntent找到潛在的適合啟動的組件的信息,并以ResolveInfo類的對象的形式返回結(jié)果,如果返回null,表示當(dāng)前系統(tǒng)中沒有任何組件可以接收并處理該sendIntent。如果返回不是null,就表明系統(tǒng)中至少存在一個組件可以接收并處理該sendIntent,只有在這種情況下,我們才會執(zhí)行代碼startActivity(sendIntent),在通過intent啟動組件之前先判斷要啟動的組件存不存在是個良好的編程習(xí)慣,因?yàn)槿绻到y(tǒng)中不存在支持你的intent的組件,那么當(dāng)你調(diào)用startActivity()、startService()、bindService()等方法的時候,Android就會拋出異常。
四、強(qiáng)制用戶使用App Chooser
在上文中我們已經(jīng)提到,如果我們的Intent是隱式的,當(dāng)我們通過startActivity(intent)嘗試啟動組件的時候,可能Android系統(tǒng)會顯示上面的截圖文件詢問用戶要啟動哪個App,有時候用戶會將某一個App設(shè)置為默認(rèn)的App,這樣下次我們再執(zhí)行代碼startActivity(intent)的時候就有可能不會再出現(xiàn)選擇App的界面,而是直接運(yùn)行上次用戶設(shè)置為默認(rèn)App的應(yīng)用。這對于用戶選擇一個默認(rèn)瀏覽器打開網(wǎng)頁這種情形是有好處的,因?yàn)橐话阋粋€用戶習(xí)慣于用一個自己喜歡的瀏覽器。
但是如果用戶不想每次都用同一個默認(rèn)App處理這樣的情形怎么辦呢?這時候我們可以在代碼中明確地使用App選擇對話框,比如黨我們的App執(zhí)行一個action為ACTION_SEND的分享功能時,我們想讓用戶分享自己數(shù)據(jù)的代碼,但是我們不確定用戶想通過哪個App去分享,我們想每次都彈出App選擇對話框讓用戶決定想通過哪個App分享,示例代碼如下所示:
Intent sendIntent = new Intent(Intent.ACTION_SEND); ... String title = "請選擇想通過哪個App分享數(shù)據(jù)"; // 驗(yàn)證是否有App能夠接收并處理sendIntent if (sendIntent.resolveActivity(getPackageManager()) != null) { // 根據(jù)sendIntent創(chuàng)建一個需要顯示App選擇對話框的intent對象 Intent chooserIntent = Intent.createChooser(sendIntent, title); // 我們使用chooserIntent作為startActivity()方法的參數(shù),而非sendIntent startActivity(chooserIntent); }
首先我們創(chuàng)建了我們原始的sendIntent,并對其設(shè)置action等相關(guān)信息,然后我們將sendIntent傳遞給了Intent.createChooser()方法中,創(chuàng)建了另一個chooserIntent。后面我們通過調(diào)用Intent.resolveActivity(PackageManager)方法判斷系統(tǒng)中是否有App能夠接收并處理sendIntent,該方法與上面之前提到過的PackageManager的resolveActivity()方法是等價的。最后我們使用chooserIntent作為startActivity()方法的參數(shù),而非sendIntent,chooserIntent會讓Android系統(tǒng)強(qiáng)制顯示用戶選擇App處理Intent的界面。
本文大部分參考了Android中對Intent部分的Develop Guide的描述,希望本文對大家更好地使用Intent對象有所幫助。
- Android控件系列之TextView使用介紹
- android textview 顯示html方法解析
- Android的TextView與Html相結(jié)合的具體方法
- android Textview文字監(jiān)控(Textview使用方法)
- Android設(shè)置TextView顯示指定個數(shù)字符,超過部分顯示...(省略號)的方法
- Android中實(shí)現(xiàn)為TextView添加多個可點(diǎn)擊的文本
- android中Intent傳值與Bundle傳值的區(qū)別詳解
- android中intent傳遞list或者對象的方法
- Android Intent的幾種用法詳細(xì)解析
- Android Intent啟動別的應(yīng)用實(shí)現(xiàn)方法
- Android開發(fā)之TextView使用intent傳遞信息,實(shí)現(xiàn)注冊界面功能示例
相關(guān)文章
Android仿美團(tuán)分類下拉菜單實(shí)例代碼
這篇文章主要為大家詳細(xì)介紹了Android仿美團(tuán)分類下拉菜單實(shí)例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-05-05Android實(shí)現(xiàn)listview滑動時漸隱漸現(xiàn)頂部欄實(shí)例代碼
android中實(shí)現(xiàn)listview滑動時漸隱漸現(xiàn)頂部欄只是在獲取listview的滑動距離上可能沒法直接獲取,需要動態(tài)的去計算。感興趣的朋友一起看看吧2016-10-10Android實(shí)現(xiàn)基于滑動的SQLite數(shù)據(jù)分頁加載技術(shù)(附demo源碼下載)
這篇文章主要介紹了Android實(shí)現(xiàn)基于滑動的SQLite數(shù)據(jù)分頁加載技術(shù),涉及Android針對SQLite數(shù)據(jù)的讀取及查詢結(jié)果的分頁顯示功能相關(guān)實(shí)現(xiàn)技巧,末尾還附帶demo源碼供讀者下載參考,需要的朋友可以參考下2016-07-07android實(shí)用工具類分享(獲取內(nèi)存/檢查網(wǎng)絡(luò)/屏幕高度/手機(jī)分辨率)
這篇文章主要介紹了android實(shí)用工具類,包括獲取內(nèi)存、檢查網(wǎng)絡(luò)、屏幕高度、手機(jī)分辨率、獲取版本號等功能,需要的朋友可以參考下2014-03-03Android studio配置lambda表達(dá)式教程
Java 8的一個大亮點(diǎn)是引入Lambda表達(dá)式,使用它設(shè)計的代碼會更加簡潔。接下來通過本文給大家介紹Android studio配置lambda表達(dá)式教程,需要的朋友參考下吧2017-05-05百度語音識別(Baidu Voice) Android studio版本詳解
這篇文章主要介紹了百度語音識別(Baidu Voice) Android studio版本詳解的相關(guān)資料,需要的朋友可以參考下2016-09-09Android自定義控件之水平圓點(diǎn)加載進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了Android自定義控件之水平圓點(diǎn)加載進(jìn)度條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-06-06