Intent解析
基于組件的架構體系,除了有定義良好的組件,如何把這些組件組裝在一起,也是一門藝術。在Android中,Intent(貌似通常譯作:意圖...),就是連接各組件的橋梁。
前段時間看同事們做Symbian平臺的
網(wǎng)易掌上郵(真的是做的用心,NB的一米,熱情歡迎所有163郵箱的S60v3用戶,猛點擊之...),有個功能是為郵件添加附件,比如你想要通過郵件發(fā)送一副圖片泡mm,可能需要有個很直觀的方式從本地選一副珍藏美圖,抑或是拿相機來個完美自拍。在Symbian中,這樣的功能,都需要你用底層的API,自己一點點寫。為了讓選圖片體驗更好,可能需要做一個類似于圖片瀏覽器之類的東西,為了把拍照做的更為順暢,甚至需要實現(xiàn)從聚焦到調節(jié)亮度之類一整套的相機功能。
而其實呢,用戶的手機中可能本身就裝了其他的專業(yè)圖片瀏覽器、相機等應用,這些應用已經(jīng)非常出色好用,而用戶也已然能很純屬使用它們,如果能進行調用,對郵箱的開發(fā)者和用戶而言,都會是個更好的選擇。但在Symbian這樣殘敗的系統(tǒng)里,應用和應用之間的結合能力奇弱無比,想復用,基本比登天還難,作為開發(fā)者,只能忍住一次又一次的惡心,為了用戶,做這些重復造輪子吃力不討好的附加工作。
還好還好,在Android中,一切變得美好多了,它將開發(fā)者從接口和對象的細節(jié)中解救出來,讓我們有更多精力投入到核心功能的開發(fā)中去。在Android中,如果你需要選個圖拍個片,只需要構造一個描述你此項意愿的Intent,發(fā)送出去,系統(tǒng)會幫你選擇一個能夠處理該項業(yè)務的組件來滿足你的需求,而不再需要糾結在具體的接口和實現(xiàn)上,Perfect World,便應如此。
Intent構成
Intent被譯作意圖,其實還是很能傳神的,Intent期望做到的,就是把實現(xiàn)者和調用者完全解耦,調用者專心將以意圖描述清晰,發(fā)送出去,就可以夢想成真,達到目的。當然,這么說太虛了,庖丁解牛,什么東西切開來看看,也許就清晰了。
Intent(
reference/android/content/Intent.html),在Android中表現(xiàn)成一個類,發(fā)起一個意圖,你需要構造這樣一個對象,并為下列幾項中的一些進行賦值:
- Action。當日常生活中,描述一個意愿或愿望的時候,總是有一個動詞在其中。比如:我想做三個俯臥撐;我要看一部x片;我要寫一部血淚史,之類云云。在Intent中,Action就是描述看、做、寫等動作的,當你指明了一個Action,執(zhí)行者就會依照這個動作的指示,接受相關輸入,表現(xiàn)對應行為,產(chǎn)生符合的輸出。在Intent類中,定義了一批量的動作,比如ACTION_VIEW,ACTION_PICK,之類的,基本涵蓋了常用動作,整一個降龍十八掌全集。當然,你也可以與時俱進,創(chuàng)造新的動作,比如lou這樣的。與系統(tǒng)預定義的相比,這些自定義動作的流通范圍很是有限,除非做了非常NB的應用,大家都需要follow你,否則通常都是應用內部流通。
- Data。當然,光有動作還是不夠的,還需要有更確切的對象信息。比如,同樣是泡這個動作,但泡咖啡,和泡妞,就差之千里了。Data的描述,在Android中,表現(xiàn)成為一個URI。用在內部通信中,可能描述是Content Provider用的形如content://xxxx這樣的東東,抑或是外部的一個形如tel://xxxx這樣的鏈接。總而言之,是能夠清楚準確的描述一個數(shù)據(jù)地址的uri。
- Type。說了Data,就必須要提Type,很多時候,會有人誤解,覺著Data和Type的差別,就猶如泡妞和泡馬子之間的差別一樣,微乎其微。但其實不然,Type信息,是用MIME來表示的,比如text/plain,這樣的東西。說到這里,兩者差別就很清晰了,Data就是門牌號,指明了具體的位置,具體問題具體分析,而type,則是強調物以類聚,解決一批量的問題。實際的例子是這樣的,比如,從某個應用撥打一個電話,會發(fā)起的是action為ACTION_DIAL且data為tel:xxx這樣的Intent,對應的人類語言就是撥打xxx的電話,很具象。而如果使用type,就寬泛了許多,比如瀏覽器收到一個未知的MIME類型的數(shù)據(jù)(比如一個視頻...),就會放出這樣的Intent,求系統(tǒng)的其他應用來幫助,表達成自然語言應該就是:查看pdf類文檔,這樣的。
- Category。通過Action,配合Data或Type,很多時候可以準確的表達出一個完整的意圖了,但也會有些時候,還需要加一些約束在里面才能夠更精準。比如,如果你雖然很喜歡做俯臥撐,但一次做三個還只是在特殊的時候才會發(fā)生,那么你可能表達說:每次吃撐了的時候,我都想做三個俯臥撐。吃撐了,這就對應著Intent的Category的范疇,它給所發(fā)生的意圖附加一個約束。在Android中,一個實例是,所有應用主Activity(就是單獨啟動時候,第一個運行的那個Activity...),都需要能夠接受一個Category為CATEGORY_LAUNCHER,Action為ACTION_Main的意圖。
- Component。在此之前,我們企圖用Action,Data/Type,Category去描述一個意圖,這是Android推薦,并期望大家在大多數(shù)時候使用的,這樣模式在Android中稱做Implicit Intents,通過這種模式,提供一種靈活可擴展的模式,給用戶和第三方應用一個選擇權。比如,還是一個郵箱軟件,他大部分功能都好,就是選擇圖片的功能做的很土,怎么辦?如果它采用的是Implicit Intents,那么它就是一個開放的體系了,手機中沒有其他圖片選擇程序的話,可以繼續(xù)使用郵箱默認的,如果有,你可以任意選擇來替代原有模塊完整這功能,一切都自然而然。但這種模式,也不是沒有成本,需要付出的是一些性能上的開銷,因為畢竟有一個檢索過程。于是,Android提供了另一種模式,叫做Explicit Intents,就需要Component的幫助了。Component就是類名,完整的,形如com.xxxxx.xxxx,一旦指明了,一切都清晰了,找的到這個類(當然會是一個特定的子類...),成功,反之,失敗。這個好處,自然是速度,適合在你明確知道這就是一個內部模塊的時候,使用它。
- Extras。通過上面的這些項,識別問題,基本完美解決了,剩下一個重要的問題,就是傳參。Extras是用來做這個事情的,它是一個Bundle類的對象,有一組可序列化的key/value對組成。每一個Action,都會有與之對應的key和value類型約定,發(fā)起Intent的時候,需要按照要求把Data不能表示的額外參數(shù)放入Extras中(當然,如果不需要額外附加參數(shù),就算了...),否則執(zhí)行者拿到的時候會抓狂的。
- Flags。能識別,有輸入,整個Intent基本就完整了,但還有一些附件的指令,需要放在Flags中帶過去。顧名思義,F(xiàn)lags是一個整形數(shù),有一些列的標志位構成,這些標志,是用來指明運行模式的。比如,你期望這個意圖的執(zhí)行者,和你運行在兩個完全不同的任務中(或說進程也無妨吧...),就需要設置FLAG_ACTIVITY_NEW_TASK的標志位。
有了上述這些,一個Intent的形象就躍然紙上了,如此豐富的內容,決定了它比傳統(tǒng)的模式,都來得強大。
Intent匹配
上次在moto dev上,聽人做Android的講座,下面有很多聽客都對Intent這個概念表示出了強烈的興趣,拿出自己熟悉領域的各類概念進行類比,比如事件、消息之類。當時我在想,Intent作為組件間的通信協(xié)定,與一般的簡單的通信方式不同,首先,從前面部分可以看到,它的描述是針對需求而不是實現(xiàn)者來進行的。其次,它的解析是依托第三方而不是兩方直接進行。
這個概念和設計模式中的中介模式(
Mediator Pattern)是一脈的,即所有的外圍組件,都只和系統(tǒng)的核心模塊發(fā)生聯(lián)系,通過它進行中轉,組件之間不直接勾搭。
如上圖所示,要想跑通整個流程,另一個很重要的東西,就是Intent Filters,它是用來描述一個Activity或Serveice等組件,期望能夠響應怎么樣的Intent。如果一個組件,只希望別的組件通過Explicit Intents(就是指明Component...)的方式來找到它,那么就不需要添加Intent Filters,反之,一定需要一個或若干個Intent Filters。Intent Filter的各個項,猶如Intent照鏡子過來的效果,包括Action,Catagory,Data,Type等。系統(tǒng)核心的模塊,會負責收集這些Intent Filters,和它們對應的組件信息。當請求者需要一個組件幫忙,并構造了描述它需求的Intent發(fā)送到系統(tǒng)核心,系統(tǒng)核心會將其與已知的各個Intent Filters進行匹配,挑選一個符合需求的組件返回。如果有多個符合的,會嘗試看看有沒有默認執(zhí)行的,如果沒有默認的,就會構造UI,讓用戶幫助抉擇,如是,整個流程就跑通了。
Intent實現(xiàn)
上圖,是請求一個Activity組件的簡單實現(xiàn)流程圖,算是用的最多的Intent解析實例。流程從調用Context.startActivity(Intent)開始,調用者傳入構造好的Intent對象,然后流程會讓實際的執(zhí)行者,是Instrumentation對象來完成。它是整個應用激活的Activity管理者,集中負責該應用內所有Activity的起承轉合生離死別。它有一個隱藏的方法execStartActivity方法,就是負責根據(jù)Intent啟動Activity的。去掉一些細節(jié),它做得最重要的事情,就是將此調用,通過RPC的方式,傳遞到ActivityManagerService。
前面一直再說,系統(tǒng)核心層,其實這里所謂的系統(tǒng)核心層,就是負責Android一些關鍵事務的一組服務。它們同樣運行在虛擬機上,和普通的Service實現(xiàn)機理是一致的,只不過它們不拋頭露臉只是默默的在下層服務,故謂之核心嘛。AcitivityManagerService,是負責Activity調度的服務,也許日后提及調度細節(jié)的時候還會有涉及。在這里,AcitivityManagerService會分兩個步驟完成相關操作,首先把Intent遞交給另一個服務PackageManagerService,此服務掌握整個軟件包及其各組件的信息,它會將傳遞過來的Intent,與已知的所有Intent Filters進行匹配(如果帶有Component信息,就不用比了...),找到了,就把相關Component的信息通知回AcitivityManagerService,在這里,會完成啟動Activity這個很多細節(jié)的事情。
由此可知,啟動Activity,要經(jīng)過多個服務的處理,并不是非常輕量的過程,在Android隨機文檔
介紹性能的一節(jié)中,對此有一個評估。但這樣的操作不是會放在循環(huán)里反復折磨的那種,因此整體效果與其付出的性能代價相比,覺得是物超所值的。