三種應用程序基本組件——activity, service和broadcast receiver——是使用稱爲intent的消息來激活的。Intent消息傳遞是一種組件間運行時綁定的機制. intent是Intent對象, 它包含了須要作的操做的描述, 或者, 對於廣播來講, 包含了正在通知的消息內容. 對於向這三種組件發送intent有不一樣的機制: html
在上述三種狀況下, android系統會本身找到合適的activity, service, 或者 broadcast receivers來響應intent. 三者的intent相互獨立互不干擾. android
一個intent對象包含了接受該intent的組件的信息(例如須要的動做和該動做須要的數據)和android系統所須要的信息(例如該組件的類別,以及如何啓動它). 具體的說: windows
組件名稱
組件名稱是可選的. 若是設定了的話, Intent對象會被傳給指定的類的一個實例. 若是不設定, 則android使用其它信息來定位合適的目標. 瀏覽器
組件名稱是使用setComponent(), setClass(),或 setClassName()來設定, 使用 getComponent()來獲取. 安全
Action常量 | 目標組件 |
動做 |
---|---|---|
ACTION_CALL | activity | 發起一個電話呼叫. |
ACTION_EDIT | activity | 顯示數據給用戶來編輯. |
ACTION_MAIN | activity | 將該activity做爲一個task的第一個activity啓動,不傳入參數也不指望返回值. |
ACTION_SYNC | activity | 將設備上的數據和一個服務器同步. |
ACTION_BATTERY_LOW | broadcast receiver | 發出電量不足的警告. |
ACTION_HEADSET_PLUG | broadcast receiver | 一個耳機正被插入或者拔出. |
ACTION_SCREEN_ON | broadcast receiver | 屏幕被點亮. |
ACTION_TIMEZONE_CHANGED | broadcast receiver | 時區設置改變. |
你也能夠定義本身的action字符串用來啓動你的應用程序. 自定義的action應該包含應用程序的包名.例如"com.example.project.SHOW_COLOR". 服務器
action很大程度上決定了intent的另外部分的結構, 就像一個方法名決定了它接受的參數和返回值同樣. 所以, 最好給action一個最能反映其做用的名字. 網絡
一個intent對象中的action是使用getAction()和setAction()來讀寫的. app
Data
當將一個intent和一個組件相匹配時, 除了URI外數據類型也很重要. 例如, 一個顯示圖片的程序不該該用來處理聲音文件. 編輯器
數據類型經常能夠從URI推斷, 特別是content:URI, 它表示該數據屬於一個content provider. 但數據類型也能夠被intent對象顯示聲明. setData()方法設置URI, 而setType()方法指定MIME類型, setDataAndType()設置數據URI和MIME類型. 它們可使用getData()和getType()來讀取. ide
Category常量 | 含義 |
---|---|
CATEGORY_BROWSABLE | 目標activity可使用瀏覽器來顯示-例如圖片或電子郵件消息. |
CATEGORY_GADGET | 該activity能夠被包含在另一個裝載小工具的activity中. |
CATEGORY_HOME | 該activity顯示主屏幕,也就是用戶按下Home鍵看到的界面. |
CATEGORY_LAUNCHER | 該activity能夠做爲一個任務的第一個activity,而且列在應用程序啓動器中. |
CATEGORY_PREFERENCE | 該activity是一個選項面板. |
addCategory()方法爲一個intent對象增長一個category, removeCategory刪除一個category, getCategories()獲取intent全部的category.
Extras
intent對象有一系列put...()和set...()方法來設定和獲取附加信息. 這些方法和Bundle對象很像. 事實上附加信息可使用putExtras()和getExtras()做爲Bundle來讀和寫.
Flags各類標誌. 不少標誌指示android系統如何啓動一個activity(例如該activity屬於哪一個任務)和啓動後如何處理它(例如, 它是否屬於最近activity列表中).
android系統和應用程序使用intent對象來送出系統廣播和激活系統定義的組件.
intent有兩種:
Android將顯式intent發送給指定的類. intent對象中名字惟一決定接受intent的對象.
對於隱式intent, android系統必須找到最合適的組件來處理它. 它比較intent的內容和intent filter. intent filter是組件的一個相關結構, 表示其接受intent的能力. android系統根據intent filter打開能夠接受intent的組件. 若是一個組件沒有intent filter, 那麼它只能接受顯式intent. 若是有, 則能同時接受兩者.
當一個intent和intent filter比較時, 只考慮三個屬性: action, data, category.
extra和flag在intent解析中沒有用.
activity, service和broadcast receiver能夠有多個intent filter來告知系統它們能接受什麼樣的隱式intent. intent filter的名字很形象: 它過濾掉不想接受的intent, 留下想接受的intent. 顯式intent無視intent filter.
一個組件對能作的每件事有單獨的filter. 例如, 記事本程序的NoteEditor activity有兩個filter -- 一個啓動並顯示一個特定的記錄給用戶查看或編輯, 另外一個啓動一個空的記錄給用戶編輯.
一個intent filter是IntentFilter類的實例, 可是它通常不出如今代碼中,而是出如今android Manifest文件中, 以<intent-filter>的形式. (有一個例外是broadcast receiver的intent filter是使用 Context.registerReceiver()來動態設定的, 其intent filter也是在代碼中建立的.)
一個filter有action, data, category等字段. 一個隱式intent爲了能被某個intent filter接受, 必須經過3個測試. 一個intent爲了被某個組件接受, 則必須經過它全部的intent filter中的一個.
Action 測試
一個intent對象只能指定一個action, 而一個intent filter能夠指定多個action. action列表不能爲空, 不然它將組織全部的intent.
一個intent對象的action必須和intent filter中的某一個action匹配, 才能經過.
若是intent filter的action列表爲空, 則不經過.
若是intent對象不指定action, 而且intent filter的action列表不爲空, 則經過.
Category 測試
<intent-filter . . . >
<category android:name="android.intent.category.DEFAULT" />
注意前面說到的對於action和category的常數是在代碼中用的,而不是manifest文件中用的. 例如,CATEGORY_BROWSABLE常數對應xml中的表示爲"android.intent.category.BROWSABLE".
一個intent要經過category測試, 那麼該intent對象中的每一個category都必須和filter中的某一個匹配.
理論上來講, 一個intent對象若是沒有指定category的話, 它應該能經過任意的category 測試. 有一個例外: android把全部的傳給startActivity()的隱式intent看作至少有一個category: "android.intent.category.DEFAULT". 所以, 想要接受隱式intent的activity必須在intent filter中加入"android.intent.category.DEFAULT". ("android.intent.action.MAIN" 和"android.intent.category.LAUNCHER"的intent filter例外. 它們不須要"android.intent.category.DEFAULT".)
每一個<data>元素指定了一個URI和一個數據類型. URI每一個部分爲不一樣的屬性 -- scheme, host, port, path:
scheme://host:port/path
例如, 在以下的URI中:
content://com.example.project:200/folder/subfolder/etc
scheme爲"content", host爲"com.example.project", port爲"200", path爲"folder/subfolder/etc". host和port一塊兒組成了URI authority. 若是host未指定,則port被忽略.
這些屬性都是可選的,但它們並不是相互獨立: 要使一個authority有意義,必須指定一個scheme. 要使一個path有意義, 必須指定一個scheme和一個authority.
當intent對象中的URI和intent filter中相比較時, 它只和filter中定義了的部分比較. 例如, 若是filter中之定義了scheme,那麼全部包含該scheme的URI的intent對象都經過測試.對於path來講,可使用通配符來進行部分匹配.
<data>元素的type屬性指定了數據類型. 它在filter中比在URI中更常見. intent對象和filter均可以使用"*"通配符做爲子類型. 例如"text/*" "audio/*"表示全部的子類型都匹配.
data測試的規則以下:
若是一個intent能夠經過多於一個activity或者service的filter, 那麼用戶可能會被詢問須要啓動哪個. 若是一個都沒有的話, 那麼會拋出異常.
上述的最後一個規則(d)說明了組件一般能夠從文件和content provider中獲取數據. 所以, 它們的filter能夠只列出數據類型不列scheme. 這是個特殊狀況. 下列<data>元素告訴android該組件能夠從一個content provider取得圖像數據並顯示之:
<data android:mimeType="image/*" />
因爲大部分可用的數據由content provider提供, 指定數據類型但不指定uri的filter是最多見的狀況.
另一個常見的配置是filter具備一個scheme和一個數據類型. 例如, 下列<data>元素告訴android該component能夠從網絡獲取圖像數據並顯示之:
<data android:scheme="http" android:type="video/*" />
考慮用戶點擊一個網頁時瀏覽器的動做. 它首先試圖顯示這個數據(當作一個html頁來處理). 若是沒法顯示, 則建立一個隱式intent, 並啓動一個能夠處理它的activity. 若是沒有這樣的activity, 那麼它請求下載管理器來下載該數據. 而後它將數據置於一個content provider的控制之下, 這樣有不少activity(擁有隻有數據類型的filter)能夠處理這些數據.
大部分應用程序還有一種方法來單獨啓動, 不須要引用任何特定的數據. 這些能啓動應用程序的activity具備action爲"android.intent.action.MAIN" 的filter. 若是它們須要在應用程序啓動器中顯示, 它們必須指定"android.intent.category.LAUNCHER" 的category.
<intent-filter . . . >
intent和intent filter相匹配, 不只爲了尋找並啓動一個目標組件, 也是爲了尋找設備上組件的信息. 例如, android系統啓動了應用程序啓動器, 該程序位於屏幕的頂層, 顯示了用戶能夠啓動的程序, 這是經過查找設備上全部的action爲"android.intent.action.MAIN" ,category爲"android.intent.category.LAUNCHER"的intent filter所在的activity實現的. 而後它顯示了這些activity的圖標和標題. 相似的, 它經過尋找 "android.intent.category.HOME"的filter來定位主屏幕程序.
應用程序能夠用相同的方式來使用intent匹配. PackageManager 有一組query...()方法來尋找接受某個特定intent的全部組件, 還有一系列resolve...()方法來決定響應一個intent的最佳組件. 例如, queryIntentActivities()返回一個activity列表, 這些activity能夠執行傳入的intent. 相似的還有queryIntentServices()和queryIntentBroadcastReceivers().
記事本示例程序讓用戶能夠瀏覽一個筆記列表, 查看, 編輯, 刪除和增長筆記. 這一節關注該程序定義的intent filter.
在其manifest文件中, 記事本程序定義了三個activity, 每一個有至少一個intent filter. 它還定義了一個content provider來管理筆記數據. manifest 文件以下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
第一個activity, NoteList, 和其它activity不一樣, 由於它操做一個筆記的目錄(筆記列表), 而不是一個單獨的筆記. 它通常做爲該程序的初始界面. 它能夠作如下三件事:
該filter聲明瞭記事本應用程序的主入口. 標準的MAIN action是一個不須要任何其它信息(例如數據等)的程序入口, LAUNCHER category表示該入口應該在應用程序啓動器中列出.
該filter聲明瞭改activity能夠對一個筆記目錄作的事情. 它容許用戶查看或編輯該目錄(使用VIEW和EDIT action), 或者選取特定的筆記(使用PICK action).
<data>元素的mimeType指定了這些action能夠操做的數據類型. 它代表該activity能夠從一個持有記事本數據的content provider(vnd.google.note)取得一個或多個數據項的Cursor(vnd.android.cursor.dir).
注意該filter提供了一個DEFAULT category. 這是由於 Context.startActivity() 和Activity.startActivityForResult()方法將全部的intent都做爲做爲包含了DEFAULT category來處理, 只有兩個例外:
所以, 除了MAIN和LAUNCHER的filter以外, DEFAULT category是必須的.
這個filter描述了該activity可以在不須要知道目錄的狀況下返回用戶選擇的一個筆記的能力. GET_CONTENT action和PICK action相相似. 在這二者中, activity都返回用戶選擇的筆記的URI. (返回給調用startActivityForResult()來啓動NoteList activity的activity.) 在這裏, 調用者指定了用戶選擇的數據類型而不是數據的目錄.
這個數據類型, vnd.android.cursor.item/vnd.google.note, 表示了該activity能夠返回的數據類型 -- 一個筆記的URI. 從返回的URI, 調用者能夠從持有筆記數據的content provider(vnd.google.note)獲得一個項目(vnd.android.cursor.item)的Cursor.
也就是說, 對於PICK來講, 數據類型表示activity能夠給用戶顯式的數據類型.對於GET_CONTENT filter, 它表示activity能夠返回給調用者的數據類型.
下列intent能夠被NoteList activity接受:
action: android.intent.action.MAIN
第二個activity, NoteEditor, 爲用戶顯示一個筆記並容許他們編輯它. 它能夠作如下兩件事:
這個activity的主要目的是使用戶編輯一個筆記--VIEW或者EDIT一個筆記. (在category中,EDIT_NOTE是EDIT的同義詞.) intent包含匹配MIME類型vnd.android.cursor.item/vnd.google.note的URI--也就是某一個特定的筆記的URI. 它通常來講是NoteList activity中的PICK或者GET_CONTENT action返回的.
像之前同樣,該filter列出了DEFAULT category.
該activity的第二個目的是使用戶可以建立一個新的筆記, 並插入到已存在的筆記目錄中. 該intent包含了匹配vnd.android.cursor.dir/vnd.google.note的URI, 也就是筆
有了這些能力, NoteEditor就能夠接受如下intent:
action: android.intent.action.VIEW最後一個activity, TitleEditor, 容許用戶編輯筆記的標題. 這能夠經過直接調用activity(在intent中設置組件名稱)的方式來實現. 可是這裏咱們用這個機會來展現如何在已有數據上進行另外的操做(相似於windows中的打開方式->程序列表 -- 譯者注):
<intent-filter android:label="@string/resolve_title">
除了支持DEFAULT category以外,title編輯器還支持了另外兩個category: ALTERNATIVE和SELECTED_ALTERNATIVE. 這些category標誌着activity能夠在選項菜單中呈現給用戶(就像LAUNCHER category表示activity能夠在程序啓動器中同樣). 注意filter還提供了一個顯示標籤(android:label="@string/resolve_title")來更好的控制用戶在選項菜單中看到的內容.
有了這些能力, 如下的intent就能夠被TitleEditor接受:
action: com.android.notepad.action.EDIT_TITLE