編輯:Android開發實例
1、概述
一個應用程序的三個核心組件——activities、services、broadcast receivers,都是通過叫做intents的消息激活。Intent消息是一種同一或不同應用程序中的組件之間延遲運行時綁定的機制。intent本身(是一個Intent對象),是一個被動的數據結構保存一個將要執行的操作的抽象描述,或在廣播的情況下,通常是某事已經發生且正在宣告。對於這三種組件,有獨立的傳送intent的機制:
在每種情況下,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類定義了一些動作常量,如下:
Constant
Target component
Action
ACTION_CALL
activity
Initiate a phone call.
ACTION_EDIT
activity
Display data for the user to edit.
ACTION_MAIN
activity
Start up as the initial activity of a task, with no data input and no returned output.
ACTION_SYNC
activity
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
broadcast receiver
A headset has been plugged into the device, or unplugged from it.
ACTION_SCREEN_ON
broadcast receiver
The screen has been turned on.
ACTION_TIMEZONE_CHANGED
broadcast receiver
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類定義的一些種類常量,如下這些:
Constant
Meaning
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可以分為兩組:
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)中以<intent-filter>元素設置。但有一個例外,廣播接收者的過濾器通過調用Context.registerReceiver()動態地注冊,它直接創建一個IntentFilter對象。
一個過濾器有對應於Intent對象的動作、數據、種類的字段。過濾器要檢測隱式intent的所有這三個字段,其中任何一個失敗,Android系統都不會傳遞intent給組件。然而,因為一個組件可以有多個intent過濾器,一個intent通不過組件的過濾器檢測,其它的過濾器可能通過檢測。
清單文件中的<intent-filter>元素以<action>子元素列出動作,例如:
<intent-filter . . . > <action android:name="com.example.project.SHOW_CURRENT" /> <action android:name="com.example.project.SHOW_RECENT" /> <action android:name="com.example.project.SHOW_PENDING" /> . . . </intent-filter>
像例子所展示,雖然一個Intent對象僅是單個動作,但是一個過濾器可以列出不止一個。這個列表不能夠為空,一個過濾器必須至少包含一個<action>子元素,否則它將阻塞所有的intents。
要通過檢測,Intent對象中指定的動作必須匹配過濾器的動作列表中的一個。如果對象或過濾器沒有指定一個動作,結果將如下:
類似的,清單文件中的<intent-filter>元素以<category>子元素列出種類,例如:
<intent-filter . . . > <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> . . . </intent-filter>
注意本文前面兩個表格列舉的動作和種類常量不在清單文件中使用,而是使用全字符串值。例如,例子中所示的"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"到種類列表,也可以不包含。
類似的,清單文件中的<intent-filter>元素以<data>子元素列出數據,例如:
<intent-filter . . . > <data android:mimeType="video/mpeg" android:scheme="http" . . . /> <data android:mimeType="audio/mpeg" android:scheme="http" . . . /> . . . </intent-filter>
每個<data>元素指定一個URI和數據類型(MIME類型)。它有四個屬性scheme、host、port、path對應於URI的每個部分:
scheme://host:port/path
這四個屬性都是可選的,但它們之間並不都是完全獨立的。要讓authority有意義,scheme必須也要指定。要讓path有意義,scheme和authority也都必須要指定。
例如,下面的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也被忽略。
當比較intent對象和過濾器的URI時,僅僅比較過濾器中出現的URI屬性。例如,如果一個過濾器僅指定了scheme,所有有此scheme的URIs都匹配過濾器;如果一個過濾器指定了scheme和authority,但沒有指定path,所有匹配scheme和authority的URIs都通過檢測,而不管它們的path;如果四個屬性都指定了,要都匹配才能算是匹配。然而,過濾器中的path可以包含通配符來要求匹配path中的一部分。
<data>元素的type屬性指定數據的MIME類型。Intent對象和過濾器都可以用"*"通配符匹配子類型字段,例如"text/*","audio/*"表示任何子類型。
數據檢測既要檢測URI,也要檢測數據類型。規則如下:
如果一個Intent能夠通過不止一個活動或服務的過濾器,用戶可能會被問那個組件被激活。如果沒有目標找到,會產生一個異常。
上面最後一條規則表明組件能夠從文件或內容提供者獲取本地數據。因此,它們的過濾器僅列出數據類型且不必明確指出content:和file: scheme的名字。這是一種典型的情況,一個<data>元素像下面這樣:
<data android:mimeType="image/*" />
告訴Android這個組件能夠從內容提供者獲取image數據並顯示它。因為大部分可用數據由內容提供者(content provider)分發,過濾器指定一個數據類型但沒有指定URI或許最通用。
另一種通用配置是過濾器指定一個scheme和一個數據類型。例如,一個<data>元素像下面這樣:
<data android:scheme="http" android:type="video/*" />
告訴Android這個組件能夠從網絡獲取視頻數據並顯示它。考慮,當用戶點擊一個web頁面上的link,浏覽器應用程序會做什麼?它首先會試圖去顯示數據(如果link是一個HTML頁面,就能顯示)。如果它不能顯示數據,它將把一個隱式Intent加到scheme和數據類型,去啟動一個能夠做此工作的活動。如果沒有接收者,它將請求下載管理者去下載數據。這將在內容提供者的控制下完成,因此一個潛在的大活動池(他們的過濾器僅有數據類型)能夠響應。
大部分應用程序能啟動新的活動,而不引用任何特別的數據。活動有指定"android.intent.action.MAIN"的動作的過濾器,能夠啟動應用程序。如果它們出現在應用程序啟動列表中,它們也指定"android.intent.category.LAUNCHER"種類:
<intent-filter . . . > <action android:name="code android.intent.action.MAIN" /> <category android:name="code android.intent.category.LAUNCHER" /> </intent-filter>
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()。
Android由於其代碼是放在dalvik虛擬機上的托管代碼,所以能夠很容易的將其反編譯為我們可以識別的代碼。 之前我寫過一篇文章反編譯Android的apk包到
我們在前面介紹過Hello world示例,這裡的Hello world 的Layout定義稍有不
Android提供了許多方法來控制播放的音頻/視頻文件和流。其中該方法是通過一類稱為MediaPlayer。Android是提供MediaPlayer類訪問內置的媒體播放
Android性能優化-布局優化 今天,繼續Android性能優化 一 編碼細節優化。 編碼細節,對於程序的運行效率也是有很多的影響的。今天這篇主題由於