




版權(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025-2030年中國(guó)民用飛機(jī)內(nèi)部清潔和細(xì)節(jié)服務(wù)行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030年中國(guó)模內(nèi)電子產(chǎn)品(IME)行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030年中國(guó)植物基水行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030年中國(guó)棉制嬰兒外衣行業(yè)市場(chǎng)發(fā)展分析與發(fā)展趨勢(shì)及投資風(fēng)險(xiǎn)研究報(bào)告
- 2025-2030年中國(guó)柵極雙極晶體管STATCOM行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030年中國(guó)染料升華油墨行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025年執(zhí)業(yè)護(hù)士考試試題分析及答案
- 護(hù)理職業(yè)道德的重要性及其影響試題及答案
- 2025-2030年中國(guó)智能汽車(智能網(wǎng)聯(lián)汽車)行業(yè)市場(chǎng)發(fā)展分析及前景趨勢(shì)研究報(bào)告
- 行政管理與文化創(chuàng)新試題及答案
- 市級(jí)優(yōu)質(zhì)公開課一元一次不等式組說課課件
- 婚前醫(yī)學(xué)檢查證明
- 巴氏染色-臨床實(shí)踐能力訓(xùn)練考核標(biāo)準(zhǔn)
- 婦科經(jīng)帶胎產(chǎn)雜99方方歌講解學(xué)習(xí)
- 重慶郵電大學(xué)本科畢業(yè)設(shè)計(jì)(論文)參考模板-2020版
- 20XX-煙草車輛運(yùn)輸方案計(jì)劃
- 蘇教版二年級(jí)下冊(cè)數(shù)學(xué)競(jìng)賽試卷
- CRH380B動(dòng)車組電氣系統(tǒng)綜述綜述
- 晶體幾何基礎(chǔ)
- 作業(yè)準(zhǔn)備驗(yàn)證及停工后驗(yàn)證規(guī)定
- 控制電纜敷設(shè)、接線施工方案
評(píng)論
0/150
提交評(píng)論