天天看點

Android元件的通訊-Intent

1、概述

一個應用程式的三個核心元件——activities、services、broadcast receivers,都是通過叫做intents的消息激活。Intent消息是一種同一或不同應用程式中的元件之間延遲運作時綁定的機制。intent本身(是一個Intent對象),是一個被動的資料結構儲存一個将要執行的操作的抽象描述,或在廣播的情況下,通常是某事已經發生且正在宣告。對于這三種元件,有獨立的傳送intent的機制:

Activity:一個intent對象傳遞給Context.startActivity()或Activity.startActivityForRestult()去啟動一個活動或使一個已存在的活動去做新的事情。

Service:一個intent對象傳遞給Context.startService()去初始化一個service或傳遞一個新的指令給正在運作的service。類似的,一個intent可以傳遞給Context.bindService()去建立調用元件和目标服務之間的連接配接。

Broadcast Receiver:一個intent對象傳遞給任何廣播方法(如Context.sendBroadcast(),Context.sendOrderedBroadcast(),Context.sendStickyBroadcast()),都将傳遞到所有感興趣的廣播接收者。

在每種情況下,Android系統查找合适的activity、service、broadcast receivers來響應意圖,如果有必要的話,初始化他們。這些消息系統之間沒有重疊,即廣播意圖僅會傳遞給廣播接收者,而不會傳遞活動或服務,反之亦然。

下面首先描述intent對象,然後介紹Android将intent映射到相應元件的規則——如何解決哪個元件應該接收intent消息。對于沒有指定目标元件名字的intent,這個處理過程包括按照intent filters比對每個潛在的目标對象。

一個Intent對象是一個捆資訊,包含對intent有興趣的元件的資訊(如要執行的動作和要作用的資料)、Android系統有興趣的資訊(如處理intent元件的分類資訊和如何啟動目标活動的指令)。下面列出它的主要資訊:

處理intent的元件的名字。這個字段是一個ComponentName對象——是目标元件的完全限定類名(如"com.example.project.app.FreneticActivity")和應用程式所在的包在清單檔案中的名字(如"com.example.project")的組合。其中元件名字中的包部分不必一定和清單檔案中的包名一樣。

元件名字是可選的,如果設定了,intent對象傳遞到指定類的執行個體;如果沒有設定,Android使用intent中的其它資訊來定位合适的目标元件(見下面的Intent解析)。元件的名字通過setComponent(),setClass()或setClassName()設定,通過getComponent()讀取。

一個字元串命名的動作将被執行,或在廣播intent中,已發生動作且正被報告。Intent類定義了一些動作常量,如下:

<b>Constant</b>

<b>Target component</b>

<b>Action</b>

ACTION_CALL

activity

Initiate a phone call.

ACTION_EDIT

Display data for the user to edit.

ACTION_MAIN

Start up as the initial activity of a task, with no data input and no returned output.

ACTION_SYNC

Synchronize data on a server with data on the mobile device.

ACTION_BATTERY_LOW

broadcast receiver

A warning that the battery is low.

ACTION_HEADSET_PLUG

A headset has been plugged into the device, or unplugged from it.

ACTION_SCREEN_ON

The screen has been turned on.

ACTION_TIMEZONE_CHANGED

The setting for the time zone has changed.

檢視更多的動作請參考Intent類。其它的動作定義在Android API中,我們還可以定義自己的動作字元串一再我們的應用程式中激活元件。自定義動作字元串應該包含應用程式報名字首,如"com.example.project.SHOW_COLOR"。

動作很大程度上決定了剩下的intent如何建構,特别是資料(data)和附加(extras)字段,就像一個方法名決定了參數和傳回值。正是這個原因,應該盡可能明确指定動作,并緊密關聯到其它intent字段。換句話說,應該定義你的元件能夠處理的Intent對象的整個協定,而不僅僅是單獨地定義一個動作。

一個intent對象的動作通過setAction()方法設定,通過getAction()方法讀取。

資料(data)是将作用于其上的資料的URI和資料的MIME類型。不同的動作有不同的資料規格。例如,如果動作字段是ACTION_EDIT,資料字段将包含将顯示用于編輯的文檔的URI;如果動作是ACTION_CALL,資料字段将是一個tel:URI和将撥打的号碼;如果動作是ACTION_VIEW,資料字段是一個http:URI,接收活動将被調用去下載下傳和顯示URI指向的資料。

當比對一個intent到一個能夠處理資料的元件,通常知道資料的類型(它的MIME類型)和它的URI很重要。例如,一個元件能夠顯示圖像資料,不應該被調用去播放一個音頻檔案。

在許多情況下,資料類型能夠從URI中推測,特别是content:URIs,它表示位于裝置上的資料且被内容提供者(content provider)控制。但是類型也能夠顯示地設定,setData()方法指定資料的URI,setType()指定MIME類型,setDataAndType()指定資料的URI和MIME類型。通過getData()讀取URI,getType()讀取類型。

此外,還包含關于應該處理intent的元件類型資訊。可以在一個Intent對象中指定任意數量的種類描述。Intent類定義的一些種類常量,如下這些:

<b>Meaning</b>

CATEGORY_BROWSABLE

The target activity can be safely invoked by the browser to display data referenced by a link — for example, an image or an e-mail message.

CATEGORY_GADGET

The activity can be embedded inside of another activity that hosts gadgets.

CATEGORY_HOME

The activity displays the home screen, the first screen the user sees when the device is turned on or when the HOME key is pressed.

CATEGORY_LAUNCHER

The activity can be the initial activity of a task and is listed in the top-level application launcher.

CATEGORY_PREFERENCE

The target activity is a preference panel.

更多的種類常量請參考Intent類。

addCategory()方法添加一個種類到Intent對象中,removeCategory()方法删除一個之前添加的種類,getCategories()方法擷取Intent對象中的所有種類。

額外的鍵值對資訊應該傳遞到元件處理intent。就像動作關聯的特定種類的資料URIs,也關聯到某些特定的附加資訊。例如,一個ACTION_TIMEZONE_CHANGE intent有一個"time-zone"的附加資訊,辨別新的時區,ACTION_HEADSET_PLUG有一個"state"附加資訊,辨別頭部現在是否塞滿或未塞滿;有一個"name"附加資訊,辨別頭部的類型。如果你自定義了一個SHOW_COLOR動作,顔色值将可以設定在附加的鍵值對中。

Intent對象有一系列的put…()方法用于插入各種附加資料和一系列的get…()用于讀取資料。這些方法與Bundle對象的方法類似,實際上,附加資訊可以作為一個Bundle使用putExtras()和getExtras()安裝和讀取。

有各種各樣的标志,許多訓示Android系統如何去啟動一個活動(例如,活動應該屬于那個任務)和啟動之後如何對待它(例如,它是否屬于最近的活動清單)。所有這些标志都定義在Intent類中。

Intent可以分為兩組:

顯式intent:通過名字指定目标元件。因為開發者通常不知道其它應用程式的元件名字,顯式intent通常用于應用程式内部消息,如一個活動啟動從屬的服務或啟動一個姐妹活動。

隐式intent:并不指定目标的名字(元件名字字段是空的)。隐式intent經常用于激活其它應用程式中的元件。

Android傳遞一個顯式intent到一個指定目标類的執行個體。Intent對象中隻用元件名字内容去決定哪個元件應該獲得這個intent,而不用其他内容。

隐式intent需要另外一種不同的政策。由于預設指定目标,Android系統必須查找一個最适合的元件(一些元件)去處理intent——一個活動或服務去執行請求動作,或一組廣播接收者去響應廣播聲明。這是通過比較Intent對象的内容和intent過濾器(intent filters)來完成的。intent過濾器關聯到潛在的接收intent的元件。過濾器聲明元件的能力和界定它能處理的intents,它們打開元件接收聲明的intent類型的隐式intents。如果一個元件沒有任何intent過濾器,它僅能接收顯示的intents,而聲明了intent過濾器的元件可以接收顯示和隐式的intents。

隻有當一個Intent對象的下面三個方面都符合一個intent過濾器:action、data(包括URI和資料類型)、category,才被考慮。附加資訊和标志在解析哪個元件接收intent中不起作用。

活動、服務、廣播接收者為了告知系統能夠處理哪些隐式intent,它們可以有一個或多個intent過濾器。每個過濾器描述元件的一種能力,即樂意接收的一組intent。實際上,它篩掉不想要的intents,也僅僅是不想要的隐式intents。一個顯式intent總是能夠傳遞到它的目标元件,不管它包含什麼;不考慮過濾器。但是一個隐式intent,僅當它能夠通過元件的過濾器之一才能夠傳遞給它。

一個元件的能夠做的每一工作有獨立的過濾器。例如,記事本中的NoteEditer活動有兩個過濾器,一個是啟動一個指定的記錄,使用者可以檢視和編輯;另一個是啟動一個新的、空的記錄,使用者能夠填充并儲存。

一個intent過濾器是一個IntentFilter類的執行個體。因為Android系統在啟動一個元件之前必須知道它的能力,但是intent過濾器通常不在java代碼中設定,而是在應用程式的清單檔案(AndroidManifest.xml)中以&lt;intent-filter&gt;元素設定。但有一個例外,廣播接收者的過濾器通過調用Context.registerReceiver()動态地注冊,它直接建立一個IntentFilter對象。

一個過濾器有對應于Intent對象的動作、資料、種類的字段。過濾器要檢測隐式intent的所有這三個字段,其中任何一個失敗,Android系統都不會傳遞intent給元件。然而,因為一個元件可以有多個intent過濾器,一個intent通不過元件的過濾器檢測,其它的過濾器可能通過檢測。

清單檔案中的&lt;intent-filter&gt;元素以&lt;action&gt;子元素列出動作,例如:

像例子所展示,雖然一個Intent對象僅是單個動作,但是一個過濾器可以列出不止一個。這個清單不能夠為空,一個過濾器必須至少包含一個&lt;action&gt;子元素,否則它将阻塞所有的intents。

要通過檢測,Intent對象中指定的動作必須比對過濾器的動作清單中的一個。如果對象或過濾器沒有指定一個動作,結果将如下:

如果過濾器沒有指定動作,沒有一個Intent将比對,所有的intent将檢測失敗,即沒有intent能夠通過過濾器。

如果Intent對象沒有指定動作,将自動通過檢查(隻要過濾器至少有一個過濾器,否則就是上面的情況了)

類似的,清單檔案中的&lt;intent-filter&gt;元素以&lt;category&gt;子元素列出種類,例如:

注意本文前面兩個表格列舉的動作和種類常量不在清單檔案中使用,而是使用全字元串值。例如,例子中所示的"android.intent.category.BROWSABLE"字元串對應于本文前面提到的BROWSABLE常量。類似的,"android.intent.action.EDIT"字元串對應于ACTION_EDIT常量。

對于一個intent要通過種類檢測,intent對象中的每個種類必須比對過濾器中的一個。即過濾器能夠列出額外的種類,但是intent對象中的種類都必須能夠在過濾器中找到,隻有一個種類在過濾器清單中沒有,就算種類檢測失敗!

是以,原則上如果一個intent對象中沒有種類(即種類字段為空)應該總是通過種類測試,而不管過濾器中有什麼種類。但是有個例外,Android對待所有傳遞給Context.startActivity()的隐式intent好像它們至少包含"android.intent.category.DEFAULT"(對應CATEGORY_DEFAULT常量)。是以,活動想要接收隐式intent必須要在intent過濾器中包含"android.intent.category.DEFAULT"。

注意:"android.intent.action.MAIN" 和 "android.intent.category.LAUNCHER"設定,它們分别标記活動開始新的任務和帶到啟動清單界面。它們可以包含"android.intent.category.DEFAULT"到種類清單,也可以不包含。

類似的,清單檔案中的&lt;intent-filter&gt;元素以&lt;data&gt;子元素列出資料,例如:

每個&lt;data&gt;元素指定一個URI和資料類型(MIME類型)。它有四個屬性scheme、host、port、path對應于URI的每個部分: 

<code>scheme://host:port/path  例如,下面的URI:  <code>content://com.example.project:200/folder/subfolder/etc</code>  scheme是content,host是"com.example.project",port是200,path是"folder/subfolder/etc"。host和port一起構成URI的憑據(authority),如果host沒有指定,port也被忽略。</code> 這四個屬性都是可選的,但它們之間并不都是完全獨立的。要讓authority有意義,scheme必須也要指定。要讓path有意義,scheme和authority也都必須要指定。

當比較intent對象和過濾器的URI時,僅僅比較過濾器中出現的URI屬性。例如,如果一個過濾器僅指定了scheme,所有有此scheme的URIs都比對過濾器;如果一個過濾器指定了scheme和authority,但沒有指定path,所有比對scheme和authority的URIs都通過檢測,而不管它們的path;如果四個屬性都指定了,要都比對才能算是比對。然而,過濾器中的path可以包含通配符來要求比對path中的一部分。

&lt;data&gt;元素的type屬性指定資料的MIME類型。Intent對象和過濾器都可以用"*"通配符比對子類型字段,例如"text/*","audio/*"表示任何子類型。

資料檢測既要檢測URI,也要檢測資料類型。規則如下:

一個Intent對象既不包含URI,也不包含資料類型:僅當過濾器也不指定任何URIs和資料類型時,才不能通過檢測;否則都能通過。

一個Intent對象包含URI,但不包含資料類型:僅當過濾器也不指定資料類型,同時它們的URI比對,才能通過檢測。例如,mailto:和tel:都不指定實際資料。

一個Intent對象包含資料類型,但不包含URI:僅當過濾也隻包含資料類型且與Intent相同,才通過檢測。

一個Intent對象既包含URI,也包含資料類型(或資料類型能夠從URI推斷出):資料類型部分,隻有與過濾器中之一比對才算通過;URI部分,它的URI要出現在過濾器中,或者它有content:或file: URI,又或者過濾器沒有指定URI。換句話說,如果它的過濾器僅列出了資料類型,元件假定支援content:和file: 。

如果一個Intent能夠通過不止一個活動或服務的過濾器,使用者可能會被問那個元件被激活。如果沒有目标找到,會産生一個異常。

上面最後一條規則表明元件能夠從檔案或内容提供者擷取本地資料。是以,它們的過濾器僅列出資料類型且不必明确指出content:和file: scheme的名字。這是一種典型的情況,一個&lt;data&gt;元素像下面這樣:

告訴Android這個元件能夠從内容提供者擷取image資料并顯示它。因為大部分可用資料由内容提供者(content provider)分發,過濾器指定一個資料類型但沒有指定URI或許最通用。

另一種通用配置是過濾器指定一個scheme和一個資料類型。例如,一個&lt;data&gt;元素像下面這樣:

告訴Android這個元件能夠從網絡擷取視訊資料并顯示它。考慮,當使用者點選一個web頁面上的link,浏覽器應用程式會做什麼?它首先會試圖去顯示資料(如果link是一個HTML頁面,就能顯示)。如果它不能顯示資料,它将把一個隐式Intent加到scheme和資料類型,去啟動一個能夠做此工作的活動。如果沒有接收者,它将請求下載下傳管理者去下載下傳資料。這将在内容提供者的控制下完成,是以一個潛在的大活動池(他們的過濾器僅有資料類型)能夠響應。

大部分應用程式能啟動新的活動,而不引用任何特别的資料。活動有指定"android.intent.action.MAIN"的動作的過濾器,能夠啟動應用程式。如果它們出現在應用程式啟動清單中,它們也指定"android.intent.category.LAUNCHER"種類:

Intents對照着Intent過濾器比對,不僅去發現一個目标元件去激活,而且去發現裝置上的元件的其他資訊。例如,Android系統填充應用程式啟動清單,最高層螢幕顯示使用者能夠啟動的應用程式:是通過查找所有的包含指定了"android.intent.action.MAIN"的動作和"android.intent.category.LAUNCHER"種類的過濾器的活動,然後在啟動清單中顯示這些活動的圖示和标簽。類似的,它通過查找有"android.intent.category.HOME"過濾器的活動發掘主菜單。

我們的應用程式也可以類似的使用這種Intent比對方式。PackageManager有一組query…()方法傳回能夠接收特定intent的所有元件,一組resolve…()方法決定最适合的元件響應intent。例如,queryIntentActivities()傳回一組能夠給執行指定的intent參數的所有活動,類似的queryIntentServices()傳回一組服務。這兩個方法都不激活元件,它們僅列出所有能夠響應的元件。對應廣播接收者也有類似的方法——queryBroadcastReceivers()。

本文轉自linzheng 51CTO部落格,原文連結:http://blog.51cto.com/linzheng/1080677

繼續閱讀