馬上加入Android 台灣中文網,立即免費下載應用遊戲。
您需要 登錄 才可以下載或查看,沒有帳號?註冊
x
一、 Intent 作用
Intent被譯作意圖,其實還是很能傳神的,Intent期望做到的,就是把實現者和調用者完全解耦,調用者專心將以意圖描述清晰,發送出去,就可以夢想成真,達到目的。
Intent 是一個將要執行的動作的抽像描述,一般來說是作為參數來使用,由Intent來協助完成android各個組件之間的通訊。比如說調用 startActivity()來啟動一個activity,或者由broadcaseIntent()來傳遞給所有感興趣的 BroadcaseReceiver, 再或者由startService()/bindservice()來啟動一個後台的service.所以可以看出來,intent主要是用來啟動其他的 activity 或者service,所以可以將intent理解成activity之間的粘合劑。
二、 Intent的構成
要在不同的activity之間傳遞數據,就要在intent中包含相應的東西,一般來說數據中最基本的應該包括:
- Action: 當日常生活中,描述一個意願或願望的時候,總是有一個動詞在其中。比如:我想做 三個俯臥撐;我要看 一部x片;我要寫 一部血淚史,之類云云。在Intent中,Action就是描述看、做、寫等動作的,當你指明了一個Action,執行者就會依照這個動作的指示,接受相關輸入,表現對應行為,產生符合的輸出。在Intent類中,定義了一批量的動作,比如ACTION_VIEW,ACTION_PICK ,之類的,基本涵蓋了常用動作,整一個降龍十八掌全集
標準的Activity Actions
ACTION_MAIN 作為一個主要的進入口,而並不期望去接受數據
ACTION_VIEW 向用戶去顯示數據
ACTION_ATTACH_DATA 用於指定一些數據應該附屬於一些其他的地方,例如,圖片數據應該附屬於聯繫人
ACTION_EDIT 訪問已給的數據,提供明確的可編輯
ACTION_PICK 從數據中選取一個子項目,並返回你所選中的項目
ACTION_CHOOSER 顯示一個activity選取器,允許用戶在進程之前選取他們想要的
ACTION_GET_CONTENT 允許用戶選取特殊種類的數據,並返回(特殊種類的數據:照一張相片或錄一段音)
ACTION_DIAL 撥打一個指定的號碼,顯示一個帶有號碼的用戶界面,允許用戶去啟動呼叫
ACTION_CALL 根據指定的數據執行一次呼叫
(ACTION_CALL在應用中啟動一次呼叫有缺陷,多數應用ACTION_DIAL,ACTION_CALL不能用在緊急呼叫上,緊急呼叫可以用ACTION_DIAL來實現)
ACTION_SEND 傳遞數據,被傳送的數據沒有指定,接收的action請求用戶發數據
ACTION_SENDTO 發送一個訊息到指定的某人
ACTION_ANSWER 處理一個打進電話呼叫
ACTION_INSERT 插入一條空項目到已給的容器
ACTION_DELETE 從容器中刪除已給的數據
ACTION_RUN 執行數據,無論怎麼
ACTION_SYNC 同步執行一個數據
ACTION_PICK_ACTIVITY 為已知的Intent選取一個Activity,返回別選中的類
ACTION_SEARCH 執行一次搜索
ACTION_WEB_SEARCH 執行一次web搜索
ACTION_FACTORY_TEST 工場測試的主要進入點,
標準的廣播Actions
ACTION_TIME_TICK 當前時間改變,每分鐘都發送,不能通過組件聲明來接收,只有通過Context.registerReceiver()方法來註冊
ACTION_TIME_CHANGED 時間被設定
ACTION_TIMEZONE_CHANGED 時間區改變
ACTION_BOOT_COMPLETED 系統完成啟動後,一次廣播
ACTION_PACKAGE_ADDED 一個新應用包已經安裝在設備上,數據包括包名(最新安裝的包程式不能接收到這個廣播)
ACTION_PACKAGE_CHANGED 一個已存在的應用程式包已經改變,包括包名
ACTION_PACKAGE_REMOVED 一個已存在的應用程式包已經從設備上移除,包括包名(正在被安裝的包程式不能接收到這個廣播)
ACTION_PACKAGE_RESTARTED 用戶重新開始一個包,包的所有進程將被殺死,所有與其聯繫的執行時間狀態應該被移除,包括包名(重新開始包程式不能接收到這個廣播)
ACTION_PACKAGE_DATA_CLEARED 用戶已經清楚一個包的數據,包括包名(清除包程式不能接收到這個廣播)
ACTION_BATTERY_CHANGED 電池的充電狀態、電荷級別改變,不能通過組建聲明接收這個廣播,只有通過Context.registerReceiver()註冊
ACTION_UID_REMOVED 一個用戶ID已經從系統中移除
- Data(數據): 要事實的具體的數據,一般由一個Uri變量來表示
下面是一些簡單的例子:
ACTION_VIEW content://contacts/1 //顯示identifier為1的聯繫人的訊息。
ACTION_DIAL content://contacts/1 //給這個聯繫人打電話
除了Action和data這兩個最基本的元素外,intent還包括一些其他的元素,
- Category(範疇): 指定Action範圍,這 個選項指定了將要執行的這個action的其他一些額外的約束.有時通過Action,配合Data或Type,很多時候可以準確的表達出一個完整的意圖 了,但也會需要加一些約束在裡面才能夠更精準。比如,如果你雖然很喜歡做俯臥撐,但一次做三個還只是在特殊的時候才會發生,那麼你可能表達說:每次吃撐了 的時候 ,我都想做三個俯臥撐。吃撐了,這就對應著Intent的Category的範疇,它給所發生的意圖附加一個約束。在Android中,一個實例 是:所有應用的主Activity(單獨啟動時候,第一個執行的那個Activity...),都需要一個Category為 CATEGORY_LAUNCHER,Action為ACTION_MAIN的Intent。
- Type(數據類型): 用於指定類型,以供過濾(比如ACTION_VIEW同時指定為Type為Image,則調出瀏覽圖片的應用).一般Intent的數據類型能夠根據數據本身進行判定,但是通過設定這個屬性,可以強制採用顯式指定的類型而不再進行判定。
- Component(組件): 當 我們常用Action,Data/Type,Category去描述一個意圖,這是Android推薦,這種模式稱:Implicit Intents. 通過這種模式,提供一種靈活可擴展的模式,給用戶和第三方應用一個選取權。比如:一個郵箱軟體,大部分功能都好,就是選取圖片的功能做的很土,怎麼辦?如 果它採用的是Implicit Intents,那麼它就是一個開放的體繫了,手機中沒有其他圖片選取功能的情況下,可以繼續使用郵箱默認的,如果有,你可以任意選取來替代原有模塊完成 這功能,一切都自然而然。但這種模式需要付出性能上的開銷,因為畢竟有一個檢索過程。於是,Android提供了另一種模式,叫做Explicit Intents ,就需要Component的幫助了。Component就是完整的類名,形如com.xxxxx.xxxx ,一旦指明了,可以直接調用,自然是速度快. 適合在你明確知道這就是一個內部模塊的時候,使用它。
- Extras(附加訊息): 是其它所有附加訊息的集合。使用extras可以為組件提供擴展訊息,比如,如果要執行「發送電子郵件」這個動作,可以將電子郵件的標題、正文等保存在extras裡,傳給電子郵件發送組件。
- Flags(標誌位): 能 識別,有輸入,整個Intent基本就完整了,但還有一些附件的指令,需要放在Flags中帶過去。顧名思義,Flags是一個整形數,有一些列的標誌 位構成,這些標誌,是用來指明執行模式的。比如,你期望這個意圖的執行者,和你執行在兩個完全不同的任務中(或說進程也無妨吧...),就需要設定FLAG_ACTIVITY_NEW_TASK 的標誌位。
Android 的一個特色就是 application A 的 activity 可以啟動 application B 的 activity,儘管 A 和 B 是毫無干係的,而在用戶看來,兩個場景緊密聯繫,視覺上二者構成了一個整體。Android 就是把這種誤覺定義為 Task,它既不是 class,也不是 AndroidMainifest.xml 中的一個元素。從表現上看 Task 就像是一個 stack,一個一個的 activity 是構成 stack 的元素,做著入棧 (push) 和出棧 (pop-up)這樣簡單重複性的勞動。
默 認的規則總是滿足大多數的應用場景,但是也總會有一些例外打破習以為常的慣例。Task 的默認規則同樣並非牢不可破,修改的方法還是有的。借助 Intent 中的 flag 和 AndroidMainifest.xml 中 activity 元素的屬性,就可以控制到 Task 裡 Activity 的關聯關係和行為。
在 android.content.Intent 中一共定義了20種不同的 flag,其中和 Task 緊密關聯的有四種:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
FLAG_ACTIVITY_SINGLE_TOP
在使用這四個 flag 時,一個 Intent 可以設定一個 flag,也可以選取若干個進行組合。
默 認情況下,通過 startActivity() 啟動一個新的 Activity,新的 Activity 將會和調用者在同一個 stack 中。但是,如果在傳遞給 startActivity() 的 Intent 對像裡包含了 FLAG_ACTION_NEW_TASK,情況將發生變化,系統將為新的 Activity 「尋找」一個不同於調用者的 Task。不過要找的Task 是不是一定就是 NEW 呢?如果是第一次執行,則這個設想成立,如果說不是,也就是說已經有一個包含此 Activity 的Task 存在,則不會再啟動 Activity。
如果 flag 是 FLAG_ACTIVITY_CLEAR_TOP,同時當前的 Task 裡已經有了這個 Activity,那麼情形又將不一樣。Android 不但不會啟動新的 Activity 實例,而且還會將 Task 裡 該 Activity 之上的所有 Activity 一律結束掉,然後將 Intent 發給這個已存在的 Activity。Activity 收到 Intent 之後,可以在 onNewIntent() 裡做下一步的處理,也可以自行結束然後重新創建自己。如果 Activity 在 AndroidMainifest.xml 裡將啟動模式設定成multiple,– 默認模式,並且 Intent 裡也沒有設定 FLAG_ACTIVITY_SINGLE_TOP,那麼它將選取後者。否則,它將選取前者。FLAG_ACTIVITY_CLEAR_TOP 還可以和 FLAG_ACTION_NEW_TASK 配合使用。
如果 flag 設定的是 FLAG_ACTIVITY_SINGLE_TOP,則意味著如果 Activity 已經是執行在 Task 的 top,則該 Activity 將不會再被啟動。
三、 intent的解析
在應用中,我們可以以兩種形式來使用Intent:
- 直接Intent:指定了component屬性的Intent(調用setComponent(ComponentName)或者setClass(Context, Class)來指定)。通過指定具體的組件類,通知應用啟動對應的組件。
- 間接Intent:沒有指定comonent屬性的Intent。這些Intent需要包含足夠的訊息,這樣系統才能根據這些訊息,在在所有的可用組件中,確定滿足此Intent的組件。
對於直接Intent,Android不需要去做解析,因為目標組件已經很明確,Android需要解析的是那些間接Intent,通過解析,將 Intent映射給可以處理此Intent的Activity、IntentReceiver或Service。
Intent解析機制主 要是通過查找已註冊在AndroidManifest.xml中的所有<intent-filter>及其中定義的Intent,通過 PackageManager(註:PackageManager能夠得到當前設備上所安裝的application package的訊息) 來查找能過處理這個Intent的component。在這個解析過程中,Android是通過Intent的action、type、category 這三個屬性來進行判斷的,判斷方 法如下:
- 如果Intent指明定了action,則目標組件的IntentFilter的action列表中就必須包含有這個action,否則不能匹配;
- 如果Intent沒有提供type,系統將從data中得到數據類型。和action一樣,目標組件的數據類型列表中必須包含Intent的數據類型,否則不能匹配。
- 如果Intent中的數據不是content: 類型的URI,而且Intent也沒有明確指定它的type,將根據Intent中數據的scheme (比如 http: 或者 mailto: ) 進行匹配。同上,Intent 的scheme必須出現在目標組件的scheme列表中。
- 如果Intent指定了一個或多個category,這些類別必須全部出現在組建的類別列表中。比如Intent中包含了兩個類 別:LAUNCHER_CATEGORY 和 ALTERNATIVE_CATEGORY,解析得到的目標組件必須至少包含這兩個類別。
下面,以Android SDK中的便箋例子來說明,Intent如何定義及如何被解析。這個應用可以讓用戶瀏覽便箋列表、查看每一個便箋的詳細訊息。
Manifest.xml
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.android.notepad">
- <application android:icon="@drawable/app_notes"
- android:label="@string/app_name">
- <provider class="NotePadProvider"
- android:authorities="com.google.provider.NotePad" />
- <activity class=".NotesList"="@string/title_notes_list">
- <intent-filter>
- <action android:value="android.intent.action.MAIN"/>
- <category android:value="android.intent.category.LAUNCHER" />
- </intent-filter>
- <intent-filter>
- <action android:value="android.intent.action.VIEW"/>
- <action android:value="android.intent.action.EDIT"/>
- <action android:value="android.intent.action.PICK"/>
- <category android:value="android.intent.category.DEFAULT" />
- <type android:value="vnd.android.cursor.dir/vnd.google.note" />
- </intent-filter>
- <intent-filter>
- <action android:value="android.intent.action.GET_CONTENT" />
- <category android:value="android.intent.category.DEFAULT" />
- <type android:value="vnd.android.cursor.item/vnd.google.note" />
- </intent-filter>
- </activity>
- <activity class=".NoteEditor"="@string/title_note">
- <intent-filter android:label="@string/resolve_edit">
- <action android:value="android.intent.action.VIEW"/>
- <action android:value="android.intent.action.EDIT"/>
- <category android:value="android.intent.category.DEFAULT" />
- <type android:value="vnd.android.cursor.item/vnd.google.note" />
- </intent-filter>
- <intent-filter>
- <action android:value="android.intent.action.INSERT"/>
- <category android:value="android.intent.category.DEFAULT" />
- <type android:value="vnd.android.cursor.dir/vnd.google.note" />
- </intent-filter>
- </activity>
- <activity class=".TitleEditor"="@string/title_edit_title"
- android:theme="@android:style/Theme.Dialog">
- <intent-filter android:label="@string/resolve_title">
- <action android:value="com.google.android.notepad.action.EDIT_TITLE"/>
- <category android:value="android.intent.category.DEFAULT" />
- <category android:value="android.intent.category.ALTERNATIVE" />
- <category android:value="android.intent.category.SELECTED_ALTERNATIVE"/>
- <type android:value="vnd.android.cursor.item/vnd.google.note" />
- </intent-filter>
- </activity>
- </application>
- </manifest>
複製代碼
例子中的第一個Activity 是com.google.android.notepad.NotesList,它是應用的主入口,提供了三個功能,分別由三個 intent-filter進行描述:
1、第一個是進入便箋應用的頂級入口(action為android.app.action.MAIN)。類型為android.app.category.LAUNCHER表明這個Activity將在Launcher中列出。
2、第二個是,當type為vnd.android.cursor.dir/vnd.google.note(保存便箋記錄的目錄) 時,可以查看可用的便箋(action為android.app.action.VIEW),或者讓用戶選取一個便箋並返回給調用者(action為 android.app.action.PICK)。
3、第三個是,當type為vnd.android.cursor.item/vnd.google.note時,返回給調用者一個用戶選取的便箋 (action為android.app.action.GET_CONTENT),而用戶卻不需要知道便箋從哪裡讀取的。 有了這些功能,下面的Intent就會被解析到NotesList這個activity:
* { action=android.app.action.MAIN }:與此Intent匹配的Activity,將會被當作進入應用的頂級入口。
* { action=android.app.action.MAIN, category=android.app.category.LAUNCHER }:這是目前Launcher實際使用的 Intent,用於生成Launcher的頂級列表。
* { action=android.app.action.VIEW data=content://com.google.provider.NotePad/notes }:顯示"content://com.google.provider.NotePad/notes"下的所有便箋的列表,使用者可以遍歷列表,並且察 看某便箋的詳細訊息。
* { action=android.app.action.PICK data=content://com.google.provider.NotePad/notes }:顯示"content://com.google.provider.NotePad/notes"下的便箋列表,讓用戶可以在列表中選取一個,然後 將選取的便箋的 URL返回給調用者。
* { action=android.app.action.GET_CONTENT type=vnd.android.cursor.item/vnd.google.note }:和 上面的action為pick的Intent類似,不同的是這個Intent允許調用者(在這裡指要調用NotesList的某個Activity)指定 它們需要返回的數據類型,系統會根據這個數據類型查找合適的 Activity(在這裡系統會找到NotesList這個Activity),供用戶選取便箋。
第二個Activity是com.google.android.notepad.NoteEditor,它為用戶顯示一條便箋,並且允許 用戶修改這個便箋。它定義了兩個intent-filter,所以具有兩個功能。第一個功能是,當數據類型為 vnd.android.cursor.item/vnd.google.note時,允許用戶查看和修改一個便簽(action為 android.app.action.VIEW和android.app.action.EDIT)。第二個功能是,當數據類型為 vnd.android.cursor.dir/vnd.google.note,為調用者顯示一個新建便箋的界面,並將新建的便箋插 入到便箋列表中(action為android.app.action.INSERT)。
有了這兩個功能,下面的Intent就會被解析到NoteEditor這個activity:
* { action=android.app.action.VIEW data=content://com.google.provider.NotePad/notes/{ID} } :向用戶顯示標識為 ID的便箋。
* { action=android.app.action.EDIT data=content://com.google.provider.NotePad/notes/{ID} }:允許用戶編輯標識為ID的便箋。
* { action=android.app.action.INSERT data=content://com.google.provider.NotePad/notes }:在「content://com.google.provider.NotePad/notes」這個便箋列表中創建一個新的空便箋,並允許用 戶編輯這個便簽。當用戶保存這個便箋後,這個新便箋的URI將會返回給調用者。
最後一個Activity是com.google.android.notepad.TitleEditor,它允許用戶編輯便箋的標題。它可以被 實現為 一個應用可以直接調用(在Intent中明確設定component屬性)的類,不過這裡我們將為你提供一個在現有的數據上發佈可選操作的方法。在這個 Activity的唯一的intent-filter中,擁有一個私有的action: com.google.android.notepad.action.EDIT_TITLE,表明允許用戶編輯便箋的標題。和前面的view和edit 動作一樣,調用這個Intent 的時候,也必須指定具體的便箋(type為vnd.android.cursor.item/vnd.google.note)。不同的是,這裡顯示和編 輯的只是便箋數據中的標題。
除了支持缺省類別(android.intent.category.DEFAULT),標題編輯器還支持另外兩個標準類別: android.intent.category.ALTERNATIVE和 android.intent.category.SELECTED_ALTERNATIVE。實現了這兩個類別之後,其它 Activity就可以調用queryIntentActivityOptions(ComponentName, Intent[], Intent, int)查詢這個Activity提供的action,而不需要瞭解它的具體實現;或者調用addIntentOptions(int, int, ComponentName, Intent[], Intent, int, Menu.Item[])建立動態菜單。需要說明的是,在這個intent-filter中有一個明確的名稱(通過android:label= "@string/resolve_title"指定),在用戶瀏覽數據的時候,如果這個Activity是數據的一個可選操作,指定明確的名稱可以為用 戶提供一個更好控制界面。
有了這個功能,下面的Intent就會被解析到TitleEditor這個Activity:
* { action=com.google.android.notepad.action.EDIT_TITLE data=content://com.google.provider.NotePad/notes/{ID} }:顯示並且允許用戶編輯標識為ID的便箋的標題。
|