Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 詳解Android中Intent的使用方法

詳解Android中Intent的使用方法

編輯:關於Android編程

一、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()方法以發送自定義廣播。

二、Intent的類型

有兩種類型的Intent:explicit(顯式)的和implict(隱式)的。

顯式的Intent:如果Intent中明確包含了要啟動的組件的完整類名(包名及類名),那麼這個Intent就是explict的,即顯式的。使用顯式Intent最典型的情形是在你自己的App中啟動一個組件,因為你自己肯定知道自己的要啟動的組件的類名。比如,為了響應用戶操作通過顯式的Intent在你的App中啟動一個Activity或啟動一個Service下載文件。

隱式的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去啟動Activity或Service的時候,系統會立即啟動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實現進程間雙向通信》中最後的“注意事項”部分,裡面有相關代碼的解決方案。

三、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對其操作的一些行為,下面會介紹。

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。

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()方法。

1、顯式Intent使用示例

Intent intent = new Intent(this, ActivityB.class);
startActivity(intent);

上面的代碼在Intent的構造函數中指定了要啟動的組件的ComponentName是ActivityB,該intent對象是顯式的,調用startActivity(intent)時,Android系統會立即啟動ActivityB。

2、隱式Intent使用示例

之前提到過,在使用隱式Intent的時候需要指定其action。如果你的App不能完成某個功能,但是其他的App可能完成該功能,那麼你就可以用隱式Intent啟動其他的App去完成相應的功能。例如,你有一段文本信息,想通過其他App分享出去,那麼隱式Intent對象去啟動潛在的支持分享的App,示例代碼如下:

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就會拋出異常。

四、強制用戶使用App Chooser

在上文中我們已經提到,如果我們的Intent是隱式的,當我們通過startActivity(intent)嘗試啟動組件的時候,可能Android系統會顯示上面的截圖文件詢問用戶要啟動哪個App,有時候用戶會將某一個App設置為默認的App,這樣下次我們再執行代碼startActivity(intent)的時候就有可能不會再出現選擇App的界面,而是直接運行上次用戶設置為默認App的應用。這對於用戶選擇一個默認浏覽器打開網頁這種情形是有好處的,因為一般一個用戶習慣於用一個自己喜歡的浏覽器。

但是如果用戶不想每次都用同一個默認App處理這樣的情形怎麼辦呢?這時候我們可以在代碼中明確地使用App選擇對話框,比如黨我們的App執行一個action為ACTION_SEND的分享功能時,我們想讓用戶分享自己數據的代碼,但是我們不確定用戶想通過哪個App去分享,我們想每次都彈出App選擇對話框讓用戶決定想通過哪個App分享,示例代碼如下所示:

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對象有所幫助。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved