Android基礎(chǔ)知識(shí)資料_第1頁
Android基礎(chǔ)知識(shí)資料_第2頁
Android基礎(chǔ)知識(shí)資料_第3頁
Android基礎(chǔ)知識(shí)資料_第4頁
Android基礎(chǔ)知識(shí)資料_第5頁
已閱讀5頁,還剩27頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

Android基本概念和模型

學(xué)習(xí)在一個(gè)全新的平臺(tái)上開發(fā)最重要的第一步是什么?我覺得當(dāng)然不是語言的問題,也更不是工

具的問題。學(xué)習(xí)Windows開發(fā),我們得了解Win32窗口的運(yùn)行原理,消息隊(duì)列等機(jī)制,不了解這

些背后的機(jī)制和基本的運(yùn)行模型,只靠RAD工具我們還是只能停留在普通應(yīng)用的階段。學(xué)習(xí)Web

開發(fā),不了解HTMLDOM,HTTP協(xié)議原理等內(nèi)容,只靠掌握如ASP.Net,JSP等廠商技術(shù),我

們也做不到Web開發(fā)的“心想事成”,“胸有成竹”;同樣如AnrsBlog中說到的正則表示式的應(yīng)用,

我們還得了解其背后的匹配原理。所以,我覺得面對(duì)一個(gè)全新的平臺(tái),我們得首先了解其工作和

運(yùn)行的基本模型和原理,才能做到以后在這個(gè)平臺(tái)上的“得心應(yīng)手”。

各位看官先不要被圖形中的生詞嚇?biāo)?,接下來我?huì)詳細(xì)解釋每一個(gè)概念。

先來看圖形中的灰色部分,這部分描述了一個(gè)完整的Android應(yīng)用程序可以包含的各個(gè)組成部分,

我們將組成一個(gè)Android程序的組件稱為AndroidComponent(圖中中間部分的基類),由若干個(gè)

AndroidComponent就組成了一個(gè)完整的Android應(yīng)用程序。先看圖中左下方的Activity,這個(gè)組

件我們可以認(rèn)為它是Windows中的窗體概念,這是Android程序的基本組成部分,也就是程序的

人機(jī)交互界面。比如一個(gè)簡(jiǎn)單的短信程序就應(yīng)該包含三個(gè)Activity,一個(gè)短信列表界面,一個(gè)閱

讀短信詳細(xì)內(nèi)容的界面和一個(gè)編輯短信的界面。圖中左上角的Service顧名思議就是服務(wù),一個(gè)

Android程序中哪些部分是服務(wù)呢?舉例來說,短信程序并不只是在我們打開短信界面的時(shí)候才

去收取短信,我們退出界面后,手機(jī)仍然會(huì)去收取短信,并在新的短信到達(dá)時(shí)通知我們,所以一

定有某個(gè)任務(wù)在后臺(tái)運(yùn)行著,這就是Service了;再比如說音樂播放功能,當(dāng)我們從播放界面返回

手機(jī)待機(jī)界面的時(shí)候仍然可以繼續(xù)聽音樂,這也是一個(gè)Service的例子。其實(shí)Activity+Service是

非常常見的手機(jī)軟件應(yīng)用,比如我要做的BlogMessage同樣也是這樣的結(jié)構(gòu)。左邊中間部分的

-BroadcastReceiver”是用于接收各種系統(tǒng)定義事件或自定義事件的接收器,如果我們的程序想偵

測(cè)?些系統(tǒng)事件的發(fā)生,我們就需要寫一個(gè)BroadcastReceiver。例如我們的程序想在手機(jī)打開

Wifi的時(shí)候立即去刷新最新的數(shù)據(jù),或者我們想在手機(jī)來電時(shí)執(zhí)行某個(gè)動(dòng)作,這些都可以由

BroadcastReceiver訂閱特定的事件來完成。圖中左邊剩下的“ContentProvider”,我們可以把它理

解成一種特殊的Service,一種可以給其他程序提供數(shù)據(jù)的Service,例如手機(jī)中的聯(lián)系人信息,我

們?nèi)魏纬绦蚨伎梢院推渫ㄐ湃カ@取聯(lián)系人的信息,這就可實(shí)現(xiàn)為一個(gè)典型的ContentProvider。

再來看圖中藍(lán)色的部分,這是一個(gè)靜態(tài)的部署概念,就如同我們.Nei開發(fā)的程序集的概念一樣。

Apk是我們Android程序發(fā)布和部署的基本單位,一個(gè)完整的Android程序就可以打包為一個(gè)或

多個(gè)Apk進(jìn)行發(fā)布,我們從AndroidMarketing上下載安裝的程序也是一個(gè)個(gè)的Apk包,我們?cè)?/p>

Eclipse中的一個(gè)Project的最終Build結(jié)果也就是一個(gè)Apk文件。一個(gè)Apk中包含了上面介紹的

4種AndroidComponento

最后,圖中黃色的部分就是系統(tǒng)運(yùn)行時(shí)的概念了。由于Android平臺(tái)是基于Linux的,所以Process

(進(jìn)程)和Thread(線程)的概念和Linux中的一致,在代碼中我們可以編寫一個(gè)普通的JavaThread

來實(shí)現(xiàn)多線程。需要注意的是,Android中的Process是受系統(tǒng)自動(dòng)管理的,并不是說我們?cè)谝粋€(gè)

程序界面中按了手機(jī)上的Back鍵或者Home鍵程序就結(jié)束了,大家也很難在Android的各種程序

中找到類似Symbian程序中的“退出”功能。Android系統(tǒng)會(huì)給每一個(gè)進(jìn)程都計(jì)算出一個(gè)“重要程度”

等級(jí),在系統(tǒng)運(yùn)行的某個(gè)時(shí)候例如資源不足的時(shí)候,系統(tǒng)會(huì)根據(jù)各個(gè)進(jìn)程的“重要程度”來決定先

釋放哪個(gè)進(jìn)程。(進(jìn)程“重要程度''的判斷在Google的官方文檔還是說的比較清楚的,實(shí)際上各個(gè)

AndroidComponent都有很完整的運(yùn)行時(shí)生命周期,由于我們不太清楚進(jìn)程結(jié)束的時(shí)機(jī),了解各個(gè)

AndroidComponent的運(yùn)行時(shí)生命周期以及相關(guān)事件就對(duì)我們的開發(fā)來說非常重要,我會(huì)陸續(xù)在后

續(xù)的手記中詳細(xì)闡述這些內(nèi)容)。

一個(gè)Apk中包含的AndroidComponent在運(yùn)行時(shí)可以運(yùn)行在同一個(gè)進(jìn)程中,也可以運(yùn)行在不同

的進(jìn)程中,這取決于我們?cè)贏pk的AndroidManifest.xml上進(jìn)行的配置(大家可以將這個(gè)

AndroidManifest.xml看成是Apk的全局配制信息,其中會(huì)描述這個(gè)Apk中包含了哪些Android

Component以及各個(gè)Component的運(yùn)行和啟動(dòng)方式等,我會(huì)在后續(xù)的Post中講解這些內(nèi)容)。

最后,圖中下面中間部分的“Task”是Android中一個(gè)很特殊的運(yùn)行時(shí)概念,也是很復(fù)雜的一個(gè)

概念,Google的官方文檔用了很大的篇幅來說明這個(gè)概念。它有別于進(jìn)程和線程,并且只和

Activity的運(yùn)行時(shí)有關(guān)系。

我們可以將其理解成“窗口?!?,這是由手機(jī)上的特殊操作方式所引出的概念。由于手機(jī)上的程

序,用戶一般只能在同一時(shí)間看到一個(gè)界面,例如在編輯短信的時(shí)候一般就不能看到短信列表的

界面。而一個(gè)完整的程序一般會(huì)由多個(gè)Activity組成,所以這些Activity會(huì)在運(yùn)行時(shí)隨著打開的

先后順序會(huì)被放到同一個(gè)窗口棧(Task)中,當(dāng)前活動(dòng)窗口棧中最上面的Activity就是用戶當(dāng)前

看到的界面,按手機(jī)上的“Back”則是銷毀當(dāng)前棧頂?shù)腁ctivity,回到上一個(gè)界面。

然而Task這個(gè)概念之所以復(fù)雜,是因?yàn)椴煌琍rocess中的Activity可以被放到同一個(gè)Task中,

例如在我們的程序中可能會(huì)打開GoogleMap的地圖界面。具體Activity在運(yùn)行時(shí)該被放到哪個(gè)

Task中,這會(huì)由Activity的taskAffinity屬性決定,一般情況下一個(gè)Apk中的所有Activity在運(yùn)行

時(shí)會(huì)被放到同一個(gè)Task中,但是運(yùn)行時(shí)Activity的taskAffinity是可以修改的。例如上面說的Google

M叩的例子,地圖顯示界面默認(rèn)是存在于GoogleM叩這個(gè)程序的默認(rèn)Task中的,但是我們卻可

以在運(yùn)行時(shí)將這個(gè)界面帶到我們自己程序的當(dāng)前Task中來。窗口在Task中的“入?!焙汀俺鰲!辈?/p>

作和Activity的運(yùn)行時(shí)生命周期息息相關(guān),后面我也會(huì)用更詳細(xì)的篇幅來介紹Task和Activity運(yùn)

行時(shí)生命周期的關(guān)系。

Activity的運(yùn)行時(shí)生命周期模型

由于在Android中,進(jìn)程的生命周期大多數(shù)時(shí)候是由系統(tǒng)管理的;另外也由于手機(jī)應(yīng)用的一些特

殊性,所以我們需要更多的去關(guān)注各個(gè)AndroidComponent的運(yùn)行時(shí)生命周期模型。(所謂手機(jī)

應(yīng)用的特殊性主要是指這樣2點(diǎn):1.手機(jī)應(yīng)用的大多數(shù)情況下我們只能在手機(jī)上看到一個(gè)程序

的一個(gè)界面,用戶除了通過程序界面上的功能按鈕來在不同的窗體間切換,還可以通過Back鍵

和Home鍵來返回上一個(gè)窗口,而用戶使用Back或者Home的時(shí)機(jī)是非常不確定的,任何時(shí)候

用戶都可以使用Home或Back來強(qiáng)行切換當(dāng)前的界面。2.往往手機(jī)上一些特殊的事件發(fā)生也

會(huì)強(qiáng)制的改變當(dāng)前用戶所處的操作狀態(tài),例如無論任何情況,在手機(jī)來電時(shí),系統(tǒng)都會(huì)優(yōu)先顯示

電話接聽界面。)了解這些Component的生命周期模型一方面是讓我們對(duì)軟件在手機(jī)中的運(yùn)行

情況做到心中有數(shù),更重要的,對(duì)于程序開發(fā)來說,生命周期中的每一個(gè)關(guān)鍵事件都會(huì)有我們可

以覆寫于各種Component對(duì)應(yīng)基類型的事件處理方法,了解各Component的生命周期就是讓我

們?cè)陂_發(fā)程序時(shí)明白我們?cè)撛鯓尤ゾ帉懜鞣N事件的處理代碼。例如Activity的Create,就會(huì)有對(duì)

應(yīng)的事件處理函數(shù)onCreate,我們可以從Activity基類覆寫這個(gè)事件處理函數(shù)完成我們需要的相

關(guān)事件處理:

publicclassactMainextendsActivity{

@Override

publicvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedlnstanceState);

……〃我們的事件處理代碼

這篇Post我們就來看看最常用的Activity的運(yùn)行時(shí)生命周期模型(Service的運(yùn)行時(shí)生命周期模型

在下一篇講述了如何啟動(dòng)一個(gè)Service并和其通信后再做描述)。Activity的生命周期模型在

Google提供的官方文檔上有比較詳細(xì)的一個(gè)圖示(請(qǐng)自行翻墻查看)。其一共包含7個(gè)我們需要

關(guān)心的關(guān)鍵事件,下面對(duì)其分別詳細(xì)說明(文字中的粗體字表示后文中會(huì)經(jīng)常用到的概念在第一

次出現(xiàn)時(shí)會(huì)給出解釋,之后后文不再詳細(xì)說明):

1.voidonCreate(BundlesavedlnstanceState)

當(dāng)Activity被第首次加載時(shí)執(zhí)行。我們新啟動(dòng)一個(gè)程序的時(shí)候其主窗體的onCreate事件就會(huì)被執(zhí)

行。如果Activity被銷毀后(onDestroy后),再重新加載進(jìn)Task時(shí),其onCreate事件也會(huì)被重

新執(zhí)行。注意這里的參數(shù)savedlnstanceState(Bundle類型是一個(gè)鍵值對(duì)集合,大家可以看成

是.Net中的Dictionary)是一個(gè)很有用的設(shè)計(jì),由于前面已經(jīng)說到的手機(jī)應(yīng)用的特殊性,一個(gè)

Activity很可能被強(qiáng)制交換到后臺(tái)(交換到后臺(tái)就是指該窗體不再對(duì)用戶可見,但實(shí)際上又還是存

在于某個(gè)Task中的,比如一個(gè)新的Activity壓入了當(dāng)前的Task從而“遮蓋“住了當(dāng)前的Activity,

或者用戶按了Home鍵回到桌面,又或者其他重要事件發(fā)生導(dǎo)致新的Activity出現(xiàn)在當(dāng)前Activity

之上,比如來電界面),而如果此后用戶在一段時(shí)間內(nèi)沒有重新查看該窗體(Android通過長(zhǎng)按

Home鍵可以選擇最近運(yùn)行的6個(gè)程序,或者用戶直接再次點(diǎn)擊程序的運(yùn)行圖標(biāo),如果窗體所在

的Task和進(jìn)程沒有被系統(tǒng)銷毀,則不用重新加載Process,Task和Task中的Activity,直接重

新顯示Task頂部的Activity,這就稱之為重新查看某個(gè)程序的窗體),咳窗體連同其所在的Task

和Process則可能已經(jīng)被系統(tǒng)自動(dòng)銷毀了,此時(shí)如果再次查看該窗體,則要重新執(zhí)行onCreate事

件初始化窗體。而這個(gè)時(shí)候我們可能希望用戶繼續(xù)上次打開該窗體時(shí)的操作狀態(tài)進(jìn)行操作,而不

是一切從頭開始。例如用戶在編輯短信時(shí)突然來電,接完電話后用戶又去做了一些其他的事情,

比如保存來電號(hào)碼到聯(lián)系人,而沒有立即回到短信編輯界面,導(dǎo)致了短信編輯界面被銷毀,當(dāng)用

戶重新進(jìn)入短信程序時(shí)他可能希望繼續(xù)上次的編輯。這種情況我們就可以覆寫Activity的void

onSaveInstanceStdte(BundleoulStale)事件,通過向outSlate中寫入,些我們需要在窗體銷毀前

保存的狀態(tài)或信息,這樣在窗體重新執(zhí)行onCreate的時(shí)候,則會(huì)通過savedlnstanceState將之

前保存的信息傳遞進(jìn)來,此時(shí)我們就可以有選擇的利用這些信息來初始化窗體,而不是-一切從頭

開始。

2.voidonStart()

onCreate事件之后執(zhí)行?;蛘弋?dāng)前窗體被交換到后臺(tái)后,在用戶重新查看窗體前已經(jīng)過去了一段

時(shí)間,窗體已經(jīng)執(zhí)行了onStop事件,但是窗體和其所在進(jìn)程并沒有被銷毀,用戶再次重新查看

窗體時(shí)會(huì)執(zhí)行onRestart事件,之后會(huì)跳過onCreate事件,直接執(zhí)行窗體的onStart事件。

3.voidonResume()

onStart事件之后執(zhí)行?;蛘弋?dāng)前窗體被交換到后臺(tái)后,在用戶重新查看窗體時(shí),窗體還沒有被銷

毀,也沒有執(zhí)行過onStop事件(窗體還繼續(xù)存在于Task中),則會(huì)跳過窗體的onCreate和onStart

事件,直接執(zhí)行onResume事件。

4.voidonPause()

窗體被交換到后臺(tái)時(shí)執(zhí)行。

5.voidonStop()

onPause事件之后執(zhí)行。如果一段時(shí)間內(nèi)用戶還沒有重新查看該窗體,則該窗體的onStop事件

將會(huì)被執(zhí)行;或者用戶直接按了Back鍵,將該窗體從當(dāng)前Task中移除,也會(huì)執(zhí)行該窗體的onStop

事件。

6.voidonRestart()

onStop事件執(zhí)行后,如果窗體和其所在的進(jìn)程沒有被系統(tǒng)銷毀,此時(shí)住戶又重新查看該窗體,則

會(huì)執(zhí)行窗體的onRestart事件,onRestart事件后會(huì)跳過窗體的onCreate事件直接執(zhí)行onStart

事件。

7.voidonDestroy()

Activity被銷毀的時(shí)候執(zhí)行。在窗體的onStop事件之后,如果沒有再次查看該窗體,Activity則會(huì)

被銷毀。

最后用一個(gè)實(shí)際的例子來說明Activity的各個(gè)生命周期。假設(shè)有一個(gè)程序由2個(gè)ActivityA和

B組成,A是這個(gè)程序的啟動(dòng)界面。當(dāng)用戶啟動(dòng)程序時(shí),Process和默認(rèn)的Task分別被創(chuàng)建,接

著A被壓入到當(dāng)前的Task中,依次執(zhí)行了onCreate,onStart,onResume事件被呈現(xiàn)給了用戶;

此時(shí)用戶選擇A中的某個(gè)功能開啟界面B,界面B被壓入當(dāng)前Task遮蓋住了A,A的onPause事

件執(zhí)行,B的onCreate,onStart,onResume事件執(zhí)行,呈現(xiàn)了界面B給用戶;用戶在界面B操

作完成后,使用Back鍵回到界面A,界面B不再可見,界面B的onPause,onStop,onDestroy

執(zhí)行,A的onResume事件被執(zhí)行,呈現(xiàn)界面A給用戶。此時(shí)突然來電,界面A的onPause事件被

執(zhí)行,電話接聽界面被呈現(xiàn)給用戶,用戶接聽完電話后,又按了Home鍵回到桌面,打開另一個(gè)程

序“聯(lián)系人”,添加了聯(lián)系人信息又做了一些其他的操作,此時(shí)界面A不再可見,其onStop事件

被執(zhí)行,但并沒有被銷毀。此后用戶重新從菜單中點(diǎn)擊了我們的程序,由于A和其所在的進(jìn)程和

Task并沒有被銷毀,A的onRestart和onStart事件被執(zhí)行,接著A的onResume事件被執(zhí)行,A

又被呈現(xiàn)給了用戶。用戶這次使用完后,按Back鍵返回到桌面,A的cnPause,onSlop被執(zhí)行,

隨后A的onDestroy被執(zhí)行,由于當(dāng)前Task中己經(jīng)沒有任何Activity,A所在的Process的重要

程度被降到很低,很快A所在的Process被系統(tǒng)結(jié)束。

在Android中窗體與窗體之間如何互相調(diào)用和交換數(shù)據(jù)?窗體(Activity)和后臺(tái)的服務(wù)(Service)

如何通信?基于Unix(Linux)的系統(tǒng)都有一個(gè)很優(yōu)秀的傳統(tǒng),就是倡導(dǎo)非常輕便的進(jìn)程間通信

(IPC)機(jī)制;倡導(dǎo)進(jìn)程通過IPC來互相協(xié)作;倡導(dǎo)功能單一,小巧而強(qiáng)壯的進(jìn)程,而不是又大

又復(fù)雜的''萬金油〃。同樣,在Android中我們可以將我們的Activity和Service放在不同的進(jìn)程中

運(yùn)行,我們可以在我們的Task中加載其他進(jìn)程的Activity,這些機(jī)制都鼓勵(lì)我們''盡量利用已有

的功能,利用IPC和包含這些己有功能的程序協(xié)作,來完成一個(gè)完整的應(yīng)用〃,例如在我們的程序

中充分利用GoogleMap的相關(guān)窗體和服務(wù)。所有這些都建立在一套輕更好用的IPC機(jī)制上。

Android的組件和進(jìn)程間通信都建立在一種基于稱為Intent的消息基礎(chǔ)之上。Intent就是一種消

息,它包含了兩個(gè)重要的內(nèi)容:1.消息的目的,即這個(gè)消息是發(fā)給哪個(gè)組件的?(消息的目的中

不會(huì)包含”消息是發(fā)給哪個(gè)進(jìn)程”這樣的信息,這里Android有意淡化進(jìn)程的概念,而只讓我們關(guān)

心組件,因?yàn)榱私馓嚓P(guān)于進(jìn)程的具體信息會(huì)加大復(fù)雜度,而又如何做到進(jìn)程間的消息傳遞呢?

下文會(huì)說到一種Android中關(guān)于這點(diǎn)比較特別的設(shè)計(jì)方式,我認(rèn)為是一種簡(jiǎn)捷有用又符合手機(jī)特

點(diǎn)的設(shè)計(jì));2.消息所攜帶的數(shù)據(jù)內(nèi)容,即需要傳遞給目標(biāo)的數(shù)據(jù)。下面是一個(gè)簡(jiǎn)單的利用Intent

來啟動(dòng)一個(gè)Service并向其傳遞數(shù)據(jù)的代碼示例:

Intentserviceintent=newIntent(contextzsvrMain.class);

serviceIntent.putExtra(''Network_Report^networkstatus);

context,startService(servicelntent);

上面的代碼首先構(gòu)造了一個(gè)Intent對(duì)象,并在構(gòu)造的時(shí)候指定了這個(gè)Intent的目的地,即

wsvrMain.class/z,表示這個(gè)Intent是要傳遞給一個(gè)類名叫svrMain的Service<.然后向這個(gè)Intent

中放入了一個(gè)數(shù)據(jù),數(shù)據(jù)的key為''Network_Report〃,value為一個(gè)叫networkstatus的int類型

變量,用來指明當(dāng)前網(wǎng)絡(luò)的狀態(tài)。最后我們使用系統(tǒng)提供的上下文API,將這個(gè)Intent傳遞給指

定的Serviceo

Intent的消息目的地分為兩種模式,一種是顯式的,一種是隱式的。我們上面的例子中看到的就

是一個(gè)顯式消息的例子。顯式消息直接指定消息目的地組件的類元信息,例如上面例子中svrMain

就是我們寫的一個(gè)類名為svrMain的Service,class操作符就是獲取其類元信息。這種模式的消

息由于己經(jīng)確切知道了消息目標(biāo)的確切信息,所以只適用于同一進(jìn)程內(nèi)的不同組件之間通信,例

如打開一個(gè)子窗體,和同一進(jìn)程中的service通信等。

對(duì)應(yīng)的,隱式消息就一般用于跨進(jìn)程的通信了,隱式消息沒有確定的消息目的地,除了數(shù)據(jù)

外,隱式消息只是包含了一些用于表征消息特征的描述字段。而一些需要收到某種特定特征消息

的某個(gè)程序中的某個(gè)組件,需要通過在其所在程序的AndroidMainifest.xml中注冊(cè)一種被稱為

intent-filte「的消息特征篩選器,然后Android系統(tǒng)會(huì)按照一定的匹配規(guī)則來匹配發(fā)出的消息特征

和所有擁有響應(yīng)這種特征的intent-filter的組件(無論是同一進(jìn)程內(nèi)的組件還是不同程序中的組

件),匹配到的組件就會(huì)接受到相應(yīng)的消息。前面的描述多少有些拗口,我們舉個(gè)實(shí)際的例子來說

明,如果我們想開啟一個(gè)子窗體(無論這個(gè)窗體來自同一進(jìn)程還是不同進(jìn)程),我們除了使用顯式

消息外,我們還可以使用隱式消息:

IntentopenSomeDiaglntent=newIntent();

openSomeDiagIntent.setAction(''edwin.demo.fooActivity,z);

this.startActivity(openSomeDiaglntent);

上面的隱式消息不包含具體的目的地,而是僅包含一個(gè)名位''Action〃的特征字符串,Action就是上

文所說的Intent特征的一種。從字面上來理解,可以理解為這個(gè)消息所代表的是完成某一個(gè)動(dòng)作

的含義,由action來標(biāo)明動(dòng)作的名字。所有能夠處理這種動(dòng)作的Activity都可以收到該消息。對(duì)

應(yīng)的,可能在同?個(gè)程序或者另外的某個(gè)程序的AndroidMainifest.xml中聲明了下面這樣的?個(gè)

Activity:

<activityandroid:name=//.fooActivity/,>

<intent-fliter>

<actionandroid:name=//edwin.demo.fooActivity///>

</intent-filter>

</activity>

那么這就表示這個(gè)Activity能夠收到并處理action為"edwin.demo.fooActivity”的消息。所以上面

的代碼串起來的效果就是,打開了這個(gè)名為.fooActivity的窗口,無論這個(gè)窗口是在當(dāng)前的進(jìn)程中

還是另外的一個(gè)程序中。

除了Action這種消息特征外,Intent還有category,data這兩個(gè)特征描述屬性。Category同

樣是一個(gè)字符串,從字面上理解就是“消息的分類特征“。從程序上看其和Action的不同在于,一

個(gè)Intent只能有惟一的一個(gè)Action名稱,但是卻可以包含多個(gè)Category字符串;一個(gè)Intent-Filter

可以包含多個(gè)Action節(jié)點(diǎn)但至少要包含一個(gè),另一方面一個(gè)Intent-Filter可以包含零到多個(gè)

Category節(jié)點(diǎn)。Android在做Intenr-Filter匹配的時(shí)候,Intent的Action屬性匹配到Intent-Filter

中的任何一個(gè)action節(jié)點(diǎn),就表明擁有這個(gè)Intent-Filter的組件能夠處理這種消息;而對(duì)于

Category來講一個(gè)Intent中的所有的Category都必須存在于Intent-Filter中的Category節(jié)點(diǎn)中

時(shí),才表明匹配成功。

Data屬性可以描述一個(gè)Intent所要傳遞的數(shù)據(jù)類型和URL每一個(gè)Intent只能包含一個(gè)Data

屬性。其中數(shù)據(jù)的類型使用MIME類型描述方式來描述,例如video/mpeg表示編碼格式為mpeg

的視頻,這里也可以使用通配符,例如video/*表示任意格式的視頻文件類型;數(shù)據(jù)的URI由

scheme(協(xié)議),host,port,path四部分組成:scheme:〃host:port/path,例如

:8080/file/filel或者

content://edwin.demo.contentProvider:100/forder/contentl,其中path部分也是可以支持通配

符的。Data屬性是一個(gè)很有用的描述特征,例如下面這樣的一個(gè)包含data節(jié)點(diǎn)的Intent-Filler:

<activityandroid:name=zz.actHttpVideoMan,/>

<intent-fliter>

<actionandroid:name=,<edwin.demo.actHttpVideoMan.Mainzz/>

<dataandroid:scheme=//http,zandroid:type=wvideo/*w/>

</intent-filter>

</activity>

它表示窗體actHttpVideoMan能夠處理來自web服務(wù)器的視頻文件。這樣的filter有什么作用呢?

最典型的情況就是配合瀏覽器工作。瀏覽器在打開一個(gè)鏈接的時(shí)候首先會(huì)嘗試顯示這個(gè)鏈接對(duì)應(yīng)

的html頁面,如果這個(gè)鏈接不是一個(gè)html頁面,而是一個(gè)視頻文件或者其他瀏覽器本身不能處

理的格式的話,瀏覽器會(huì)使用隱式消息嘗試開啟?個(gè)能夠處理這種數(shù)據(jù)格式的Activity求處理,

瀏覽器發(fā)出的隱式消息就是一個(gè)包含data屬性,其中URIscheme為http,數(shù)據(jù)類型為video/*

的消息,如果有能夠匹配這個(gè)inten:的組件,例如我們上面的那個(gè)activity,瀏覽器就會(huì)啟動(dòng)這個(gè)

窗體,接著這個(gè)窗體會(huì)根據(jù)data屬性指定的URI去播放在線視頻,如果沒有可以處理這個(gè)intent

的Activity,瀏覽器才會(huì)調(diào)用下載管理器下載文件。

隱式消息這個(gè)設(shè)計(jì)簡(jiǎn)單有效,它忽略了進(jìn)程的細(xì)節(jié),讓IPC在一個(gè)更高的更接近人腦思維模式的

層次工作,讓系統(tǒng)中的不同進(jìn)程協(xié)作看起來就像是同一程序中的協(xié)作一樣,這種簡(jiǎn)單的IPC機(jī)制

在很大的程度上鼓勵(lì)我們和其他進(jìn)程協(xié)作,通過協(xié)作的進(jìn)程來完成一個(gè)復(fù)雜的任務(wù),而不是把什

么功能都做到一個(gè)大而全的程序里面。不過上文還有一些細(xì)節(jié)沒有提到,例如如果一個(gè)intent有

多個(gè)可匹配的處理組件,系統(tǒng)如何處理?這就要分響應(yīng)消息的組件類型來說了,如果是service,

那么這些service都可以啟動(dòng)并處理消息,如果是Activity,則android會(huì)彈出一個(gè)對(duì)話框讓用戶

進(jìn)行選擇。比如我們安裝了多個(gè)可以處理在線視頻的軟件,當(dāng)我們?cè)跒g覽器中點(diǎn)擊一個(gè)在線視頻

的鏈接時(shí),系統(tǒng)會(huì)讓用戶選擇使用哪個(gè)軟件來觀看。另外大家一定會(huì)想到安全性的問題,如果不

同進(jìn)程間的組件可以通過隱式消息互相通信,那我們的程序不是可以輕易調(diào)用到其他的程序或者

系統(tǒng)中的一些敏感程序的組件,這樣會(huì)不會(huì)很不安全呢?其實(shí)Android在安全方面有一個(gè)統(tǒng)一,

完備和輕便的安全策略模型,Intent的安全自然是被考慮在內(nèi)的,關(guān)于android的安全模型我會(huì)

在后續(xù)的系列blog中專門說明。

最后,除了Intent這種基于消息的進(jìn)程內(nèi)和進(jìn)程間通信模型外,android中也有一種相比起來梢

顯笨重一些的IPC機(jī)制,它采用類似遠(yuǎn)程方法調(diào)用的方案,通過接口定義文件AIDL來定義一個(gè)

IPC接口,然后通過接收方實(shí)現(xiàn)接口,調(diào)用方調(diào)用接口的本地代理實(shí)現(xiàn)來完成IPCo這種模型只

適用于Activity和Service間的通信,之所以需要這種稍顯重量的模式,是因?yàn)锳ctivity除了發(fā)送

intent去啟動(dòng)一個(gè)service外,可能還需要能夠在Service的運(yùn)行過程中連接到service,對(duì)Service

發(fā)送一些控制請(qǐng)求。例如音樂播放程序,其后臺(tái)的播放服務(wù)往往獨(dú)立運(yùn)行,以方便我們?cè)谑褂闷?/p>

他程序界面時(shí)也能聽到音樂。同時(shí)這個(gè)后臺(tái)播放服務(wù)也會(huì)定義一個(gè)控制接口,包含比如播放,暫

停,快進(jìn)之類的方法,任何時(shí)候播放程序的界面都可以通過使用bindSer/iceAPI連接到播放服務(wù),

獲取這個(gè)接口的包含IPC細(xì)節(jié)的實(shí)現(xiàn)代理,通過這組控制接口方法對(duì)其進(jìn)行控制,這時(shí)這種IPC

的方案就顯的更方便更直觀一些了。有關(guān)使用AIDL這種IPC的更詳細(xì)描述,Gooqle的官方文檔

已做了詳細(xì)的講解。

apkAPK是AndroidPackage的縮寫,即Android安裝包(anapk)。APK是類似SymbianSis或Sisx

的文件格式。通過將APK文件直接傳到Android模擬器或Android手機(jī)中執(zhí)行即可安裝。apk

文件和sis一樣最終把a(bǔ)ndroidsdk編譯的工程打包成一個(gè)安裝程序文件格式為apk。APK文件其

實(shí)是zip格式,但后綴名被修改為apk,通過UnZip解壓后,可以看到Dex文件,Dex是DalvikVM

executes的全稱,即AndroidDalvik執(zhí)行程序,并非JavaME的字節(jié)碼而是Dalvik字節(jié)碼。一個(gè)

APK文件結(jié)構(gòu)為:META-INF\Jar文件中??梢钥吹絩es\存放資源文件的目錄

AndroidManifest.xml程序全局配置文件classes.dexDalvik字節(jié)碼resources.arse編譯后的二進(jìn)制

資源文件總結(jié)下我們發(fā)現(xiàn)Android在運(yùn)行一個(gè)程序時(shí)首先需要UnZip,然后類似Symbian那樣直

接,和WindowsMobile中的PE文件有區(qū)別,這樣做對(duì)于程序的保密性和可靠性不是很高,通過

dexdump命令可以反編譯,但這樣做符合發(fā)展規(guī)律,微軟的WindowsGadgets或者說WPF也采

用了這種構(gòu)架方式。在Android平臺(tái)中dalvikvm的執(zhí)行文件被打包為叩k格式,最終運(yùn)行時(shí)加載

器會(huì)解壓然后獲取編譯后的andioidmanifest.xml文件中的permission分支相關(guān)的安全訪問,但仍

然存在很多安全限制,如果你將apk文件傳到/system/app文件夾下會(huì)發(fā)現(xiàn)執(zhí)行是不受限制的。最

終我們平時(shí)安裝的文件可能不是這個(gè)文件夾,而在androidrom中系統(tǒng)的apk文件默認(rèn)會(huì)放入這個(gè)

文件夾,它們擁有著root權(quán)限。

【三】組件入門

組件(Component),在談及所謂架構(gòu)和重用的時(shí)候,是一個(gè)重要的事情。很多時(shí)候都會(huì)說基

于組件的軟件架構(gòu),指的是期望把程序做樂高似的,有一堆接口標(biāo)準(zhǔn)封裝完整的組件放在哪里,

想用的時(shí)候取上幾個(gè)一搭配,整個(gè)程序就構(gòu)建完成了。

在開篇的時(shí)候就在說,Android是一個(gè)為組件化而搭建的平臺(tái),它引入所謂Mash-Up的概念,

這使得你在應(yīng)用的最上層,想做的不組件化都是很困難的一件事情(底層邏輯,好吧,管不了…)。

具體說來,Android有四大組件四喜丸子;Activity>ServiceBroadcastReceiver^Content

Provider,

Activity

做一個(gè)完整的Android程序,不想用到Activity,真的是比較困難的一件事情,除非是想做綠葉

想瘋了。因?yàn)锳ctivity是Android程序與用戶交互的窗口,在我看來,從這個(gè)層面的視角來看,

Android的Activity特像網(wǎng)站的頁面。

首先,一個(gè)網(wǎng)站,如果一張頁面都沒有,那…,真是一顆奇葩。而一張頁面往往都有個(gè)獨(dú)立的主

題和功能點(diǎn),比如登錄頁面,注冊(cè)頁面,管理頁面,如是。

在每個(gè)頁面里面,會(huì)放一些鏈接,已實(shí)現(xiàn)功能點(diǎn)的串聯(lián),有的鏈接點(diǎn)了,刷,跑到同一站點(diǎn)的另

一個(gè)頁面去了;有的鏈接點(diǎn)了,啾,可能跳到其他網(wǎng)站的頁面去;還有的鏈接點(diǎn)了,恩…,這次

沒跑,但當(dāng)前頁面的樣子可能有所變化了。這些模式,和Activity給人的感覺很像,只不過實(shí)現(xiàn)

策略不同罷了,畢竟Android這套架構(gòu)的核心思想,本身就來自源于Web的Mash-Up概念,

視為頁面的客戶端化,也未嘗不可。

Activity,在四大組件中,無疑是最復(fù)雜的,這年頭,一樣?xùn)|西和界面掛上了勾,都簡(jiǎn)化不了,

想一想,獨(dú)立做一個(gè)應(yīng)用有多少時(shí)間淪落在了界面上,就能琢磨清楚了。從視覺效果來看,一個(gè)

Activity占據(jù)當(dāng)前的窗口,響應(yīng)所有窗口事件,具備有控件,菜單等界面元素。從內(nèi)部邏輯來看,

Activity需要為了保持各個(gè)界面狀態(tài),需要做很多持久化的事情,還需要妥善管理生命周期,和

一些轉(zhuǎn)跳邏輯。對(duì)于開發(fā)者而言,就需要派生一個(gè)Activity的子類,然后埋頭苦干上述事情。對(duì)

于Activity的更多細(xì)節(jié),先可以參見:refe「ence/android/app/Activity.html。后續(xù),會(huì)獻(xiàn)上

更為詳盡的剖析。

Service

服務(wù),從最直白的視角來看,就是剝離了界面的Activity,它們?cè)诤芏郃ndroid的概念方面比較

接近,都是封裝有一個(gè)完整的功能邏輯實(shí)現(xiàn),只不過Service不拋頭露臉,只是默默無聲的做堅(jiān)

實(shí)的后盾。

但其實(shí),換個(gè)角度來看,Android中的服務(wù),和我們通常說的Windows服務(wù),Web的后臺(tái)服

務(wù)又有一些相近,它們通常都是后臺(tái)長(zhǎng)時(shí)間運(yùn)行,接受上層指令,完成相關(guān)事務(wù)的模塊。用運(yùn)行

模式來看,Activity是跳,從?個(gè)跳到?個(gè),呃…,這有點(diǎn)像模態(tài)對(duì)話框(或者還像web頁面

好了…),給一個(gè)輸入(抑或沒有…),然后不管不顧的讓它運(yùn)行,離開時(shí)返呵I輸出(同抑或沒

有…)。

而Service不是,它是等,等著上層連接上它,然后產(chǎn)生一段持久而纏綿的通信,這就像一個(gè)用

了Ajax頁面,看著沒啥變化,偷偷摸摸的和Service不知眉來眼去多少回了。

但和一般的Service還是有所不同,Android的Service和所有四大組件一樣,其進(jìn)程模型都是

可以配置的,調(diào)用方和發(fā)布方都可以有權(quán)利來選擇是把這個(gè)組件運(yùn)行在同一個(gè)進(jìn)程下,還是不同

的進(jìn)程下。這句話,可以拿把指甲刀刻進(jìn)腦海中去,它凸顯了Android的運(yùn)行特征。如果一個(gè)

Service,是有期望運(yùn)行在于調(diào)用方不同進(jìn)程的時(shí)候,就需要利用Android提供的RPC機(jī)制,

為其部署?套進(jìn)程間通信的策略。

Android的RPC實(shí)現(xiàn),如上圖所示(好吧,也是從SDK中拿來主義的…),無甚稀奇,基于代

理模式的一個(gè)實(shí)現(xiàn),在調(diào)用端和服務(wù)端都去生成一個(gè)代理類,做一些序列化和反序列化的事情,

使得調(diào)用端和服務(wù)器端都可以像調(diào)用一個(gè)本地接口一樣使用RPC接口。

Android中用來做數(shù)據(jù)序列化的類是Parcel,參見:/「eference/android/os/Pa「cel.html,

封裝了序列化的細(xì)節(jié),向外提供了足夠?qū)ο蠡脑L問接口,Android號(hào)稱實(shí)現(xiàn)非常高效。

還有就是AIDL(AndroidInterfaceDefinitionLanguage),一種接口定義的語言,服務(wù)的

RPC接口,可以用AIDL來描述,這樣,ADT就可以幫助你自動(dòng)生成一整套的代理模式需要用到

的類,都是想起來很乏力寫起來很苦力的那種。更多內(nèi)容,可以再看看:

guide/developing/tools/aidl.html,如果有興致,可以找些其他PRC實(shí)現(xiàn)的資料lou幾眼。

關(guān)于Service的實(shí)現(xiàn),還強(qiáng)推參看APIDemos這個(gè)Sample里面的RemoteService實(shí)現(xiàn)。

它完整的展示了實(shí)現(xiàn)一個(gè)Service需要做的事情:那就是定義好需要接受的Intent,提供同步

或異步的接口,在上層綁定了它后,通過這些接口(很多時(shí)候都是RPC的…)進(jìn)行通信。在RPC

接口中使用的數(shù)據(jù)、回調(diào)接口對(duì)象,如果不是標(biāo)準(zhǔn)的系統(tǒng)實(shí)現(xiàn)(系統(tǒng)可序列化的),則需要自定

義aidl,所有一切,在這個(gè)Sample里都有表達(dá),強(qiáng)薦。

Service從實(shí)現(xiàn)角度看,最特別的就是這些RPC的實(shí)現(xiàn)了,其他內(nèi)容,都會(huì)接近于Activity的

一些實(shí)現(xiàn),也許不再會(huì)詳述了。

BroadcastReceiver

在實(shí)際應(yīng)用中,我們常需要等,等待系統(tǒng)抑或其他應(yīng)用發(fā)出一道指令,為自己的應(yīng)用擦亮明燈指

明方向。而這種等待,在很多的平臺(tái)上,都會(huì)需要付出不小的代價(jià)。

比如,在Symbian中,你要等待一個(gè)來電消息,顯示歸屬地之類的,必須讓自己的應(yīng)用忍辱負(fù)

重偷偷摸摸的開機(jī)啟動(dòng),消隱圖標(biāo)陷藏任務(wù)項(xiàng),潛伏在后臺(tái),監(jiān)控著相關(guān)事件,等待轉(zhuǎn)瞬即逝的

出手機(jī)會(huì)。這是一件很發(fā)指的事情,不但白白耗費(fèi)了系統(tǒng)資源,還留了個(gè)流氓軟件的罵名,這真

是賣力不討好的正面典型。

在Android中,充分考慮了廣泛的這類需求,于是就有了BroadcastReceive「這樣的一個(gè)組件。

每個(gè)BroadcastReceiver都可以接收一種或若干種Intent作為觸發(fā)事件(有不知道Intent的

么,后面會(huì)知道了…),當(dāng)發(fā)生這樣事件的時(shí)候,系統(tǒng)會(huì)負(fù)責(zé)喚醒或傳遞消息到該Broadcast

Receiver,任其處置。在此之前和這以后,BroadcastReceive「是否在運(yùn)行都變得不重要了,

及其綠色環(huán)保。

這個(gè)實(shí)現(xiàn)機(jī)制,顯然是基于一種注冊(cè)方式的,BroadcastReceiver將其特征描述并注冊(cè)在系統(tǒng)

中,根據(jù)注冊(cè)時(shí)機(jī),可以分為兩類,被我冠名為冷熱插拔。所謂冷插拔,就是BroadcastReceiver

的相關(guān)信息寫在配置文件中(求配置文件詳情?稍安,后續(xù)奉上…),系統(tǒng)會(huì)負(fù)責(zé)在相關(guān)事件發(fā)

生的時(shí)候及時(shí)通知到該BroadcastReceiver,這種模式適合于這樣的場(chǎng)景。某事件方式->通

知Broadcast,啟動(dòng)相關(guān)處理應(yīng)用。比如,監(jiān)聽來電、郵件、短信之類的,都隸屬于這種模式。

而熱插拔,顧名思義,插拔這樣的事情,都是由應(yīng)用自己來處理的,通常是在OnResume事件

中通過registerReceiver進(jìn)行注冊(cè),在OnPause等事件中反注冊(cè),通過這種方式使其能夠在

運(yùn)行期間保持對(duì)相關(guān)事件的關(guān)注。比如,一款優(yōu)秀的詞典軟件(比如,有道詞典…),可能會(huì)有

在運(yùn)行期間關(guān)注網(wǎng)絡(luò)狀況變化的需求,使其可以在有廉價(jià)網(wǎng)絡(luò)的時(shí)候優(yōu)先使用網(wǎng)絡(luò)查詢?cè)~匯,在

其他情況下,首先通過本地詞庫(kù)來查詞,從而兼顧腰包和體驗(yàn),一舉兩得一石二鳥一箭雙雕(注,

真實(shí)在有道詞典中有這樣的能力,但不是通過BroadcastReceive「實(shí)現(xiàn)的,僅以為例…)。而

這樣的監(jiān)聽,只需要在其工作狀態(tài)下保持就好,不運(yùn)行的時(shí)候,管你是天大的網(wǎng)路變化,與我何

干。其模式可以歸結(jié)為:?jiǎn)?dòng)應(yīng)用->監(jiān)聽事件?>發(fā)生時(shí)進(jìn)行處理。

除了接受消息的一方有多種模式,發(fā)送者也有很重要的選擇權(quán)。通常,發(fā)送這有兩類,一個(gè)就是

系統(tǒng)本身,我們稱之為系統(tǒng)Broadcast消息,在refe「ence/android/content/Intent.html

StandardBroadcastActions,可以求到相關(guān)消息的詳情。除了系統(tǒng),自定義的應(yīng)用可以放

出Broadcast消息,通過的接口可以是Context.sendBroadcast,抑或是

Context.sendOrderedBroadcasto前者發(fā)出的稱為Normalbroadcast,所有關(guān)注該消息的

Receiver,都有機(jī)會(huì)獲得并進(jìn)行處理;后者放出的稱作Orderedbroadcasts,顧名思義,接受

者需要按資排輩,排在后面的只能吃前面吃剩下的,前面的心情不好私吞了,后面的只能喝西北

風(fēng)了。

當(dāng)BroadcastReceiver接收至U相關(guān)的消息,它們通常做一些簡(jiǎn)單的處理,然后轉(zhuǎn)化稱為一條

Notification,一次振鈴,一次震動(dòng),抑或是啟動(dòng)一個(gè)Activity進(jìn)行進(jìn)一步的交互和處理。所以,

雖然Broadcast整個(gè)邏輯不復(fù)雜,卻是足夠有用和好用,它統(tǒng)一了Android的事件廣播模型,

讓很多平臺(tái)都相形見細(xì)了。更多BroadcastReceive「相關(guān)內(nèi)容,參見:

/reference/android/content/BroadcastReceiver.htmlo

ContentProvider

ContentProvider,聽著就和數(shù)據(jù)相關(guān),沒錯(cuò),這就是Android提供的第三方應(yīng)用數(shù)據(jù)的訪問

方案。在Android中,對(duì)數(shù)據(jù)的保戶是很嚴(yán)密的,除了放在SD卡中論數(shù)據(jù),一個(gè)應(yīng)用所持有的

數(shù)據(jù)庫(kù)、文件、等等內(nèi)容,都是不允許其他直接訪問的,但有時(shí)候,溝通是必要的,不僅對(duì)第三

方很重要,對(duì)應(yīng)用自己也很重要。

比如,一個(gè)聯(lián)系人管理的應(yīng)用。如果不允許第三方的應(yīng)用對(duì)其聯(lián)系人數(shù)據(jù)庫(kù)進(jìn)行增刪該查,整個(gè)

應(yīng)用就失去了可擴(kuò)展力,必將被其他應(yīng)用拋棄,然后另立門戶,自個(gè)玩自個(gè)的去了。

Andorid當(dāng)然不會(huì)真的把每個(gè)應(yīng)用都做成一座孤島,它為所有應(yīng)用都準(zhǔn)備了一扇窗,這就是

ContentProvidero應(yīng)用想對(duì)外提供的數(shù)據(jù),可以通過派生Contentprovider類,封裝成一枚

ContentProvider,每個(gè)ContentProvider都用一個(gè)uri作為獨(dú)立的標(biāo)識(shí),形如:

content://com.xxxxxo所有東西看著像REST的樣子,但實(shí)際上,它比REST更為靈活。和

REST類似,uri也可以有兩種類型,一種是帶id的,另一種是列表的,但實(shí)現(xiàn)者不需要按照這

個(gè)模式來做,給你id的uri你也可以返回列表類型的數(shù)據(jù),只要調(diào)用者明白,就無妨,不用苛求

所謂的RESTo

另外,ContentProvider不和REST一樣只有uri可用,還可以接受Projection,Selection,

OrderBy等參數(shù),這樣,就可以像數(shù)據(jù)庫(kù)那樣進(jìn)行投影,選擇和排序。查詢到的結(jié)果,以Cursor

(參見:reference/and「oid/database/Cu「sor.html)的形式進(jìn)行返回,調(diào)用者可以移動(dòng)

Cursor來訪問各列的數(shù)據(jù)。

ContentProvider屏蔽了內(nèi)部數(shù)據(jù)的存儲(chǔ)細(xì)節(jié),向外提供了上述統(tǒng)一的接口模型,這樣的抽象

層次,大大簡(jiǎn)化了上層應(yīng)用的書寫,也對(duì)數(shù)據(jù)的整合提供了更方便的途徑。ContentProvider

內(nèi)部,常用數(shù)據(jù)庫(kù)來實(shí)現(xiàn),Android提供了強(qiáng)大的Sqlite支持,但很多時(shí)候,你也可以封裝文件

或其他混合的數(shù)據(jù)。

在Android中,ContentResolvei■是用來發(fā)起ContentProvider的定位和訪問的。不過它僅

提供了同步訪問的ContentProvide「的接口。但通常,ContentProvider需要訪問的可能是

數(shù)據(jù)庫(kù)等大數(shù)據(jù)源,效率上不足夠快,會(huì)導(dǎo)致調(diào)用線程的擁塞。因此Android提供了一個(gè)

AsyncQueryHandler(參見:reference/android/content/AsyncQueryHandler.html),

幫助進(jìn)行異步訪問ContentProvidero

在各大組件中,Service和ContentProvider都是那種需要持續(xù)訪問的。Service如果是一個(gè)

耗時(shí)的場(chǎng)景,往往會(huì)提供異步訪問的接口,而ContentProvider不論效率如何,都提供的是約

定的同步訪問接口。我想這遵循的就是場(chǎng)景導(dǎo)向設(shè)計(jì)的原則,因?yàn)镃ontentProvider僅是提供

數(shù)據(jù)訪問的,它不能確信具體的使用場(chǎng)景如何,會(huì)怎樣使用它的數(shù)據(jù);而相比之下,Service包

含的邏輯更史雜更完整,可以抉擇大部分時(shí)候使用某接口的場(chǎng)景,從而確定最貼切的接口是同步

還是異步,簡(jiǎn)化了上層調(diào)用的邏輯。

配置

四大組件說完了,四大組件幕后的英雄也該出場(chǎng)了,那就是每個(gè)應(yīng)用都會(huì)有一份的配置文件,名

稱是AndroidManifest.xml,在工程的根目錄下。在這個(gè)配置文件中,不僅會(huì)描述一些應(yīng)用相

關(guān)的信息,很重要的,會(huì)包含一個(gè)應(yīng)用中所有組件的信息。如果你派生Activity或者Service實(shí)

現(xiàn)了一個(gè)相關(guān)的類,這只是把它組件化的第一步,你需要把這個(gè)類的相關(guān)信息寫到配置文件中,

它才會(huì)作為一個(gè)組件被應(yīng)用到,否則只能默默無聞的黯淡度過余生。

擺了一幅圖出來,這次不是偷來的,是敝帚自珍原創(chuàng),所以沒有意外的畫的很丑,但基本還是可

以體現(xiàn)出一些意思。在InOthers的部分,這里是一般平臺(tái)應(yīng)用之間通信和交互的模型,每個(gè)應(yīng)

用都有很強(qiáng)烈的應(yīng)用邊界(往往表現(xiàn)為進(jìn)程邊界…),Appl的還是App2的,分得很是清楚。

每個(gè)應(yīng)用內(nèi)部,都有自己的邏輯去切分功能組件,這樣的切分通常沒有什么標(biāo)準(zhǔn),率性而為。應(yīng)

用間的交互邏輯也比較零散,Appl與App2交互,往往需要明確知道對(duì)方應(yīng)用的具體信息,

比如進(jìn)程ID,進(jìn)程名稱之類的,這樣使得應(yīng)用和應(yīng)用之間的聯(lián)系,變得很生硬。而上層應(yīng)用和系

統(tǒng)應(yīng)用的通信,往往有很多特定的模式,這種模式,很可能是無法直接應(yīng)用在普通應(yīng)用之間的,

換而言之,系統(tǒng)應(yīng)用是有一定特殊性的。

重點(diǎn),在圖的下半部,描述的是Android的應(yīng)用情形。在Android中,應(yīng)用的邊界,在組件這

個(gè)層面,是極度模糊,什么進(jìn)程、什么應(yīng)用,都可以不必感知到。舉個(gè)例子,App1,實(shí)現(xiàn)了A

和B兩個(gè)組件,App2,實(shí)現(xiàn)了C這個(gè)組件。A和C,都想使用B這個(gè)組件,那么它們的使用方

式是完全一致的,都需要通過系統(tǒng)核心的組件識(shí)別和通信機(jī)制,找到和使用組件B。A,雖說和B

是一個(gè)娘胎里蹦出來的,很不好意思,沒有任何特殊的后面和捷徑,還是要跑規(guī)矩的途徑才能用

到,一片和諧社會(huì)的景象油然而生。

在Android中,所有組件的識(shí)別和消息傳遞邏輯都必須依賴底層核心來進(jìn)行(通信可以沒有底層

核心的參與,比如一旦Service找到了,就可以和它產(chǎn)生持久的通信…),沒有底層核心的牽線

搭橋,任何兩個(gè)組件都無法產(chǎn)生聯(lián)系。比如一個(gè)Activity,跳到另一個(gè)Activity,必須要向底層

核心發(fā)起?個(gè)Intent,有底層解析并認(rèn)可后,會(huì)找到另?個(gè)Activity,把相關(guān)消息和數(shù)據(jù)傳給它。

一個(gè)Activity想使用ContentProvider中的數(shù)據(jù),必須通過底層核心解析相關(guān)的uri,定位到

S4"ContentProvider,把參數(shù)傳遞給它,然后返回Activity需要的Cursor。這樣的設(shè)計(jì),保

證了底層核心對(duì)所有組件的絕對(duì)掌控權(quán)和認(rèn)知權(quán),使得搭積木似的開發(fā)變成可能。

為了,使得核心系統(tǒng)能夠完整的掌握每個(gè)組件的信息,這就需要配置文件了。配置文件,就是將

組件插到底層核心上的這個(gè)插頭。只有通過這個(gè)插頭插在底層核心的插座上(不要亂想,非十八

禁…),組件才能夠發(fā)光發(fā)熱,閃耀光芒。

組件的配置信息在我看來主要包含兩個(gè)方面,一部分是描述如何認(rèn)知。比如,Activity.Service.

BroadcastReceiver都會(huì)有名字信息,和希望能夠把握的Intent信息(姑且看成消息好了…),

ContentProvide「會(huì)有一個(gè)描述其身份的uri。當(dāng)其他組件通過這樣的名字或者Intent,就可

以找到它。

另一部分是運(yùn)行相關(guān)的信息。這個(gè)組件,期望怎么來運(yùn)行,放在單獨(dú)的進(jìn)程,還是和調(diào)用者一個(gè)

進(jìn)程,還是找相關(guān)的其他組件擠在同一個(gè)進(jìn)程里面,這些內(nèi)容,都可以在配置的時(shí)候來決定(調(diào)

用者在這個(gè)約束范圍內(nèi),有進(jìn)一步的選擇權(quán)…)。更多配置項(xiàng),請(qǐng)參見:

guide/topics/manifest/manifest-intro.htmL

通過前續(xù)內(nèi)容,也許可以幫助大家對(duì)Android組件有個(gè)初略的了解。但這些了解都還停留在靜態(tài)

層面,程序是個(gè)動(dòng)態(tài)的概念,關(guān)于各個(gè)組件具體是怎么聯(lián)系在一起的,如何手拉手運(yùn)行起來完成

一項(xiàng)功能的,這便是后話了。

【四】一一組件調(diào)用

Intent解析

基于組件的架構(gòu)體系,除了有定義良好的組件,如何把這些組件組裝在一起,也是一門藝術(shù)。在

Android中,Intent(貌似通常譯作:意圖…),就是連接各組件的橋梁。

前段時(shí)間看同事們做Symbian平臺(tái)的網(wǎng)易掌上郵(真的是做的用心,NB的一米,熱情歡迎所

有163郵箱的S60V3用戶,猛點(diǎn)擊之…),有個(gè)功能是為郵件添加附件,比如你想要通過郵件

發(fā)送一副圖片泡mm,可能需要有個(gè)很直觀的方式從本地選一副珍藏美圖,抑或是拿相機(jī)來個(gè)完

美自拍。在Symbian中,這樣的功能,都需要你用底層的API,自己一點(diǎn)點(diǎn)寫。為了讓選圖片

體驗(yàn)更好,可能需要做一個(gè)類似于圖片瀏覽器之類的東西,為了把拍照做的更為順暢,甚至需要

實(shí)現(xiàn)從聚焦到調(diào)節(jié)亮度之類一整套的相機(jī)功能.

而其實(shí)呢,用戶的手機(jī)中可能本身就裝了其他的專業(yè)圖片瀏覽器、相機(jī)等應(yīng)用,這些應(yīng)用已經(jīng)非

常出色好用,而用戶也已然能很純屬使用它們,如果能進(jìn)行調(diào)用,對(duì)郵箱的開發(fā)者和用戶而言,

都會(huì)是個(gè)更好的選擇。但在Symbian這樣殘敗的系統(tǒng)里,應(yīng)用和應(yīng)用之間的結(jié)合能力奇弱無比,

想復(fù)用,基本比登天還難,作為開發(fā)者,只能忍住一次又一次的惡心,為了用戶,做這些重復(fù)造

輪子吃力不討好的附加工作。

還好還好,在Android中,一切變得美好多了,它將開發(fā)者從接口和對(duì)象的細(xì)節(jié)中解救出來,讓

我們有更多精力投入到核心功能的開發(fā)中去。在Android中,如果你需要選個(gè)圖拍個(gè)片,只需要

構(gòu)造一個(gè)描述你此項(xiàng)意愿的Intent,發(fā)送出去,系統(tǒng)會(huì)幫你選擇一個(gè)能夠處理該項(xiàng)業(yè)務(wù)的組件來

滿足你的需求,而不再需要糾結(jié)在具體的接口和實(shí)現(xiàn)上,PerfectWorld,便應(yīng)如此。

Intent構(gòu)成

Intent被譯作意圖,其實(shí)還是很能傳神的,Intent期望做到的,就是把實(shí)現(xiàn)者和調(diào)用者完全解

耦,調(diào)用者專心將以意圖描述清晰,發(fā)送出去,就可以夢(mèng)想成真,達(dá)到目的。

當(dāng)然,這么說太虛了,庖丁解牛,什么東西切開來看看,乜許就清晰了。Intent

(reference/android/content/Intent.html),在Android中表現(xiàn)成一個(gè)類,發(fā)起一個(gè)意圖,

你需要構(gòu)造這樣一個(gè)對(duì)象,并為下列幾項(xiàng)中的一些進(jìn)行賦值:

Actiono當(dāng)日常生活中,描述一個(gè)意愿或愿望的時(shí)候,總是有一個(gè)動(dòng)詞在其中。比如:我想做三

個(gè)俯臥撐;我要看一部x片;我要寫一部血淚史,之類云云。在Intent中,Action就是描述看、

做、寫等動(dòng)作的,當(dāng)你指明了一個(gè)Action,執(zhí)行者就會(huì)依照這個(gè)動(dòng)作的指示,接受相關(guān)輸入,表

現(xiàn)對(duì)應(yīng)行為,產(chǎn)生符合的輸出。在Intent類中,定義了一批量的動(dòng)作,比如ACTION_VIEW,

ACTION_PICK,之類的,基本涵蓋了常用動(dòng)作,整一個(gè)降龍十八掌全集。當(dāng)然,你也可以與時(shí)

俱進(jìn),創(chuàng)造新的動(dòng)作,比如lou這樣的。與系統(tǒng)預(yù)定義的相比,這些自定義動(dòng)作的流通范圍很是

有限,除非做了非常NB的應(yīng)用,大家都需要follow你,否則通常都是應(yīng)用內(nèi)部流通。

Data。當(dāng)然,光有動(dòng)作還是不夠的,還需要有更確切的對(duì)象信息。比如,同樣是泡這個(gè)動(dòng)作,但

泡咖啡,和泡妞,就差之千里了。Data的描述,在Android中,表現(xiàn)成為一個(gè)URI。用在內(nèi)部

通信中,可能描述是ContentProvider用的形如content:〃xxxx這樣的東東,抑或是外部的

一個(gè)形如tel:〃xxxx這樣的鏈接??偠灾?,是能夠清楚準(zhǔn)確的描述一個(gè)數(shù)據(jù)地址的uri。

Type.說了Data,就必須要提Type,很多時(shí)候,會(huì)有人誤解,覺著Data和Type的差別,就

猶如泡妞和泡馬子之間的差別一樣,微乎其微。但其實(shí)不然,Type信息,是用MIME來表示的,

比如text/plain,這樣的東西。說到這里,兩者差別就很清晰了,Data就是門牌號(hào),指明了具

體的位置,具體問題具體分析,而type,則是強(qiáng)調(diào)物以類聚,解決一批量的問題。實(shí)際的例子是

這樣的,比如,從某個(gè)應(yīng)用撥打一個(gè)電話,會(huì)發(fā)起的是action為ACTION_DIAL且data為

tel:xxx這樣的In

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論