Android Intent

顯示Intent和隱式Intent

Android中的Intent是一個非常重要且常用的類,可以用來在一個組件中啟動App中的另一個組件或者是啟動另一個App的組件,這里所說的組件指的是Activity、Service以及Broadcast。

  • Intent功能

    • 啟動Activity
    • 啟動Service
    • 發送廣播
  • Intent類型

    • 顯示Intent,Intent中明確包含了要啟動的組件的完整類名(包名及類名),那么這個Intent就是explict的,即顯式的。適用于在App內使用,因為開發者一定知道自己開發的組件完整類名。
    • 隱式Intent,Intent沒有包含要啟動的組件的完整類名,那么這個Intent就是implict的,即隱式的。使用情況,隱式的Intent只用在當我們想在自己的App中通過Intent啟動另一個App的組件的時候,讓另一個App的組件接收并處理該Intent。

需要注意的是,為了確保App的安全性,我們應該總是使用顯式Intent去啟動Service并且不要為該Service設置任何的Intent Filter。通過隱式的Intent啟動Service是有風險的,因為你不確定最終哪個App中的哪個Service會啟動起來以響應你的隱式Intent,更悲催的是,由于Service沒有UI的在后臺運行,所以用戶也不知道哪個Service運行了。

Intent組成

MIME TYPE

在學習Intent的組成元素之前,先來了解一下基本知識:MIME TYPE。

什么是MIME TYPE

MIME type的縮寫為(Multipurpose Internet Mail Extensions)代表互聯網媒體類型(Internet media type),MIME使用一個簡單的字符串組成,最初是為了標識郵件Email附件的類型。媒體類型通常是通過 HTTP 協議,由Web服務器告知瀏覽器的,更準確地說,是通過 Content-Type 來表示的,例如:

Content-Type: text/HTML

MIME類型能包含視頻、圖像、文本、音頻、應用程序等數據。

MIME TYPE組成

通常只有一些在互聯網上獲得廣泛應用的格式才會獲得一個 MIME Type,如果是某個客戶端自己定義的格式,一般只能以 application/x- 開頭。例如application/vnd.ms-excel指定了Microsoft Excel文件類型。

每個MIME類型由兩部分組成,前面是數據的大類別,例如聲音audio、圖象image等,后面定義具體的種類。

常見的MIME Type請參考MIME 參考手冊

MIME Type通配符

標準的MIME Type中為同一種資源的不同格式指定了不同的MIME Type。例如圖片資源MIME Type有,image/bmp、image/cis-cod、image/gif、image/ief、image/jpeg、image/pipeg...如果需要指定大致的類型,比如指定所有圖片資源,就可以使用通配符*,構成image/*。

MIME Type作用

在把輸出結果傳送到瀏覽器上的時候,瀏覽器必須啟動適當的應用程序來處理這個輸出文檔。這可以通過MIME Type來判斷啟動哪個程序來處理。在HTTP中,MIME類型被定義在Content-Type header中

Android MIME Type

Android MIME Type類型

  • 標準的MIME Type

    <data android:mimeType="image/jpeg ">

  • 自定義

    <data android:mimeType="vnd.android.cursor.item/vnd.google.note"/>

<data />屬性會在后面的Intent組成元素中介紹。自定義的MIME Type格式也是有要求的,自定義的方式涉及到ContentProvider,所以下次再仔細閱讀。

MIME Type參考

認識安卓中的MIME Type

什么是 MIME TYPE?

android中用到的MimeType的處理方式

Intent組成部分

Android可以根據Intent所攜帶的信息去查找要啟動的組件,Intent還攜帶了一些數據信息以便要啟動的組件根據Intent中的這些數據做相應的處理。

Intent由6部分信息組成:Component Name、Action、Data、Category、Extras、Flags。可以根據作用分為以下三種:

  • Component Name、Action、Data、Category,決定了Android會啟動哪個組件,其中Component Name用于在顯式Intent中使用,Action、Data、Category、Extras、Flags用于在隱式Intent中使用。
  • Extras,里面包含了具體的用于組件實際處理的數據信息。
  • Flags,其是Intent的元數據,決定了Android對其操作的一些行為。

Component Name

要啟動的組件的名稱。一般在顯示Intent中,指定要啟動的組件類文件參數,就是Component Name。如果沒有設置component name,那么該Intent就是隱式的,Android系統會根據其他的Intent的信息(例如action、data、category等)做一些比較判斷決定最終要啟動哪個組件。所以,如果你啟動一個你自己App中的組件,你應該通過指定component name通過顯式Intent去啟動它(因為你知道該組件的完整類名)。

需要注意的是,當啟動Service的時候,你應該總是指定Component Name。否則,你不確定最終哪個App的哪個組件被啟動了,并且用戶也看不到哪個Service啟動了。

Component Name參數指定

component name在Intent中對應的field是ComponentName對象,你可以通過要啟動的組件的完整類名(包括應用的包名)指定該值,例如com.example.ExampleActivity。你可以通過Intent的setComponent()方法、setClass()方法、setClassName()方法或Intent的構造函數指定component name。

Action

是表示了要執行操作的字符串,比如查看或選擇,其對應著Intent Filter中的action標簽<action />

Action定義

Action可以選用預定義的,也可以自定義。Intent類和Android中其他framework級別的一些類也提供了許多已經定義好的具有一定通用意義的action。
自定義是指定獨有的action以便于你的App中的Intent的使用或其他App中通過Intent調用你的App中的組件。

如果自定義了action,請務必將App的包名作為該action的前綴,這是一種良好的編程習慣,避免造成混淆。

設定Action

通過調用intent對象的setAction()方法或在Intent的構造函數中指定intent的action。注意Intent只能由一個action。

Data

Intent中的data指的是Uri對象和數據的MIME類型,其對應著Intent Filter中的data標簽<data />

Uri

一個完整的Uri由scheme、host、port、path組成,格式是<scheme>://<host>:<port>/<path>。Uri就像一個數據鏈接,組件可以根據此Uri獲得最終的數據來源。通常將Uri和action結合使用,比如我們將action設置為ACTION_VIEW,我們應該提供將要被編輯修改的文檔的Uri。

Android預定義的schema有很多,content協議、http協議、file協議,還有tel協議表示打電話,geo協議表示地理位置...

MIME Type

Android就會基于Uri和MIME類型將Intent傳遞給符合條件的組件。例如指定了MIME Type之后,不讓Android誤將一個含有視頻Uri的Intent對象傳遞給一個只能顯示圖片的Activity。

如果Uri使用的是content:協議,那么這就說明Uri所提供的數據將來自于本地設備,即數據由ContentProvider提供,這種情況下Android會根據Uri自動推斷出MIME類型,此種情況我們無需再自己指定MIME類型。因為此Uri的類型可以通過provider的getType(Uri)方法得到。

設置Data

如果只設置數據的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。

<category android:name="android.intent.category.DEFAULT" />在調用startActivity()或者startActivityForResult()方法時會自動給參數Intent添加category.DEFAULT。所以要在相應的IntentFilter中添加該category。

Extras

額外的數據信息。Intent中有一個Bundle對象存儲著各種鍵值對,接收該Intent的組件可以從中讀取出所需要的信息以便完成相應的工作。有的Intent需要靠Uri攜帶數據,有的Intent是靠extras攜帶數據信息。

定義Extra的鍵值

Intent類里面也指定了很多預定義的EXTRA_*形式的extra。同時也可以聲明自己自定義的extra,請確保App的包名作為你的extra的前綴。

設置Extra

通過調用Intent對象的各種重載的putExtra(key, value)方法向Intent中加入各種鍵值對形式的額外數據。你也可以直接創建一個Bundle對象,向該Bundle對象傳入很多鍵值對,然后通過調用Intent對象的putExtras(Bundle)方法將其一塊設置給Intent對象中去。

Flags

flag就是標記的意思,Intent類中定義的flag能夠起到作為Intent對象的元數據的作用。這些flag會告知Android系統如何啟動Activity(例如,新啟動的Activity屬于哪個task)以及在該Activity啟動后如何對待它(比如)。更多信息可參見Intent的setFlags()方法。

實踐

mImageUri = Uri.fromFile(outPutImage);
Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
startActivityForResult(intent, TAKE_PHOTO);

imageUri指定了圖片存儲的Uri,作為extra,讓相機知道拍照完成后照片的存儲位置信息。

使用隱式Intent最佳方式

當使用隱式Intent來啟動一個組件時,如果Android系統沒有相應的組件,會拋出異常。所以有必要在啟動組件時,先去判斷一下Android系統有無至少一個符合Intent條件的組件。判斷方式有兩種:PackageManager和Intent兩個類中的方法。

兩種方法會讓Android根據該sendIntent找到潛在的適合啟動的組件的信息,并以ResolveInfo類的對象的形式返回結果,如果返回null,表示當前系統中沒有任何組件可以接收并處理該Intent。如果返回不是null,就表明系統中至少存在一個組件可以接收并處理該Intent。

PackageManager

PackageManager pm = getPackageManager();
        //參數flag為什么賦予0,不知道
        if(pm.resolveActivity(intent, 0) != null) {
            startActivity(intent);
        }

參數Flag用于指定尋找方式,文檔中有一句話:The most important is MATCH_DEFAULT_ONLY, to limit the resolution to only those activities that support the CATEGORY_DEFAULT.指定MATCH_DEFAULT_ONLY flag用于尋找含有CATEGORY_DEFAULT的category的組件。

平時用這種方法來判斷有無符合條件的組件,flag一般設置為0,不知道為什么。我個人的理解可能是,多個flag是通過位或來組成一個flag,這里給flag設置為0,當與原始的flag進行位或運算時,不改變原始的flag,也就是保持了默認。如有誤,請指出,純屬瞎YY。

Intent

if(intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        }

App Chooser

當使用隱式Intent時,Android系統中可能有多個符合該Intent條件的組件。第一次打開時會彈出一個選擇器供用戶選擇,用戶可以勾選始終讓其中一個作為默認啟動組件。這樣下次再啟動時,就會默認打開選擇的組件。但是,如果用戶想分享某個文件時,不一定只會用一個應用來分享,所以這種情況下我們應該強制每一次都打開選擇器,讓用戶選擇。

intent = Intent.createChooser(intent, "Choose WebKit");
        if(intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        }

這里調用Intent類中靜態方法createChooser()來創建一個選擇器Intent,每一次都去啟動該Intent。

Intent過濾

設定好隱式Intent的各項參數后,需要和所有組件的IntentFilter中各項參數匹配,找到符合條件的組件。

IntentFilter

即Intent過濾器,一個組件可以包含0個或多個Intent Filter。Intent Filter是寫在App的manifest文件中的,其通過設置action或uri數據類型等指明了組件能夠處理接收的Intent的類型。如果你給你的Activity設置了Intent Filter,那么這就使得其他的App有可能通過隱式Intent啟動你的這個Activity。

當Android系統接收到一個隱式Intent要啟動一個Activity(或其他組件)時,Android會根據以下三個信息比較Intent的信息與注冊的組件的intent-filter的信息,從而為該Intent選擇出最匹配的Activity(或其他組件):

  • intent中的action
  • intent中的category
  • intent中的data(包含Uri以及data的MIME類型)

也就是隱式intent對象要分別滿足要啟動的目標組件中注冊的intent-filter中的<action />、<category />、<data />三個標簽中的信息。

IntentFilter示例

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>

        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

<activity android:name=".activity.ImplicitActivity">
    <intent-filter>
        <action android:name="android.example.com.intentusage.ACTION_START"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

<activity android:name=".activity.TakePhotoActivity">
    <intent-filter>
        <action android:name="android.example.com.intentusage.ACTION_SHOW_PIC"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

Action測試

  1. 要想讓intent對象通過action測試,那么intent-filter中聲明的action不能為空且要包含intent對象中的action值(如果intent的action值不為空的話)。
  2. 如果intent-filter沒有聲明任何action,那么所有的intent的對象(即無論intent如何配置)都無法通過intent-filter的action測試。

Category測試

  1. 如果intent對象不包含任何category,并且該intent不是用來啟動Activity的,那么該intent對象總是能通過所有任意的intent-filter的category測試;
  2. 如果intent對象包含category(至少一個),那么只有當intent-filter中聲明的category全部包含intent對象中的所有category的時候才通過category測試。
  3. 如果允許Activity被隱式的Intent啟動,那么我們必須在該Activity的intent-filter中聲明值為android.intent.category.DEFAULT的category。

android.intent.category.DEFAULT的category,這是因為當我們把一個隱式的intent傳遞給startActivity()或startActivityForResult()方法時,Android會自動給該隱式intent添加值為android.intent.category.DEFAULT的category,所以為了能讓intent-filter包含intent中全部的category,我們就需要在Activity的intent-filter中添加該category,在使用時需要特別注意。

Data測試

在IntentFilter中設置data屬性的uri值時,使用uri的通配符*#。其中*匹配任意字符串,#匹配任意數字。

參考博客Android中Intent對象與Intent Filter過濾匹配過程詳解

參考

Android中Intent概述及使用

Android中Intent對象與Intent Filter過濾匹配過程詳解

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內容