當(dāng)對(duì)Android有一些了解后,不難發(fā)現(xiàn),Android程序UI框架接近于Web頁(yè)面的概念。每一個(gè)用于呈現(xiàn)頁(yè)面的組件,Activity,都是彼此獨(dú)立的,它們通過(guò)系統(tǒng)核心來(lái)調(diào)度整合,彼此之間的通過(guò)Intent機(jī)制來(lái)串聯(lián)。
每一種架構(gòu)都會(huì)有其利弊,Android當(dāng)然也不能超然脫俗。由于Activity之間的松耦合關(guān)系,使得其復(fù)用能力特別的出色,Mash-Up方式可以有效的提高開(kāi)發(fā)效率。但另一方面,由于Activity過(guò)于的獨(dú)立,它們之間的數(shù)據(jù)共享,成為一個(gè)麻煩的事情。
基于消息的傳輸
最標(biāo)準(zhǔn)的Activity之間的數(shù)據(jù)傳輸,就是通過(guò)Intent的Extra對(duì)象。比如,你在A這個(gè)Activity上拿到一坨用戶輸入的文本信息,興高采烈的想把它放到B這個(gè)Activity上展示并發(fā)送,一個(gè)很可行的方式,是通過(guò)Intent的putExtra接口,把用戶輸入的那些字符信息,按照key/value的形式放進(jìn)Intent,傳輸?shù)紹這個(gè)Activity上。
如上圖示,從A到B的傳輸,看上去是一個(gè)直連,但其實(shí),Intent都是要經(jīng)由系統(tǒng)核心層去分析調(diào)度的,這個(gè)操作,跨越了進(jìn)程邊界,自然而然,其中的數(shù)據(jù),就是需要序列化和反序列化的,而不可以僅通過(guò)一個(gè)指針就倒騰過(guò)去了。
基于這樣類消息的傳輸模式,好處不多說(shuō),直接談問(wèn)題:
- 首先,對(duì)于大數(shù)據(jù),就是一場(chǎng)杯具,不可能一坨上M的數(shù)據(jù),也來(lái)來(lái)回回的傳來(lái)傳去,慢死了誰(shuí)來(lái)負(fù)責(zé);
- 再則,Activity之間,維系的是一種線性關(guān)系,當(dāng)我想把一份數(shù)據(jù),從隊(duì)尾一級(jí)級(jí)傳到隊(duì)頭的話,自己歷經(jīng)磨難不提,會(huì)把中間所有的Activity都搭上,他們明明自己可能不需要這份數(shù)據(jù),也得拿著擱著,為他人做嫁衣裳,不惆悵都不行;
- 此外,基于消息的傳輸,會(huì)把同一份數(shù)據(jù)生成若干個(gè)副本,有時(shí)候,這樣很好,沒(méi)有副作用,大家自己玩自己的不需要看別人臉色,但還有些時(shí)候,你就上桿子需要把這些數(shù)據(jù)都修改一下,同步起來(lái)那就太慘烈了;
- 最后,寫序列化代碼實(shí)在是太無(wú)聊了,稍微復(fù)雜點(diǎn)的代碼,就要自己寫個(gè)序列化接口,整個(gè)吃力不討好的活計(jì)。
基于外部存儲(chǔ)的傳輸
既然獸獸手手相傳太幸苦,自然而然的想法就是找個(gè)地方,A把數(shù)據(jù)擱在那里,把地址信息告訴B,B需要的話,按圖索驥,自取就好。這個(gè)擱東西的地方,可以考慮選擇外部存儲(chǔ)。
在Android中,預(yù)設(shè)了一些快捷便利類和模塊,更好的支持不同類別數(shù)據(jù)的存取。如果,需要存儲(chǔ)的是一些小數(shù)據(jù)量的配置信息,可以選擇 Preference,它等同于傳統(tǒng)意義上的設(shè)置文件。Preference提供了一些基于key/value的存取接口,可以放置一些簡(jiǎn)單的基本數(shù)據(jù)或者派生了 Parcelable接口的對(duì)象。一個(gè)很好的應(yīng)用場(chǎng)景是 Cookie的存放。你在登錄界面獲得了一份Cookie,你可以把它扔進(jìn)Preference,誰(shuí)想要誰(shuí)去拿,再也不要來(lái)來(lái)回回的折騰了。
Preference適合于小數(shù)據(jù)、設(shè)置信息,如果大數(shù)據(jù),你可以考慮使用 數(shù)據(jù)庫(kù)。在Android中,使用的是 Sqlite,相關(guān)的類,堆放在 android.database名字空間下,自查,無(wú)需贅述。
在Android里,數(shù)據(jù)庫(kù)是私有的,如果想分享給第三方組件使用,就需要用ContentProvider來(lái)封裝了。比如你用系統(tǒng)的錄音機(jī)組件即時(shí)搞一段音頻信息,它不是返回可能大到恐怖的錄音數(shù)據(jù),而是會(huì)返回給你一個(gè)Uri,它標(biāo)明了這份數(shù)據(jù)在ContentProvider的地址信息,拿著這個(gè)Uri,領(lǐng)取數(shù)據(jù)就好。
當(dāng)然當(dāng)然,如果你足夠淡定,也可以用赤果果的 File來(lái)存儲(chǔ)。如果這個(gè)文件存在手機(jī)私有目錄下,那就內(nèi)部使用,放在SD卡上,那就可以所有應(yīng)用,一切分享。
基于這樣外部存儲(chǔ)的數(shù)據(jù)傳輸,優(yōu)缺點(diǎn)顯而易見(jiàn),它解決了困擾Intent的傳輸路徑復(fù)雜,不利于傳輸大批量數(shù)據(jù)的問(wèn)題,但同時(shí),它有留下了效率隱患,復(fù)雜了編程模型。因?yàn)槊鎸?duì)外部存儲(chǔ),開(kāi)發(fā)者必須要考慮效率問(wèn)題,很多時(shí)候,多線程就會(huì)被提上議程,這樣,想不麻煩,都不行鳥(niǎo)。
基于Service的傳輸
既然存在外部太慢,那么還是在內(nèi)存級(jí)別解決問(wèn)題好了,這時(shí)候,你可能就需要請(qǐng)出Android四大組件之一的Service了。Service設(shè)計(jì)的本意,就是提供一些后臺(tái)的服務(wù),數(shù)據(jù)存取,也可以歸于其職責(zé)的一部分。
Service是提供了直連機(jī)制,調(diào)用的Activity,可以通過(guò) bindService方法,與目標(biāo)Service建立一條數(shù)據(jù)通路,拿到 IBinder。這樣,通過(guò)Android提供的IPC模型,就可以進(jìn)行遠(yuǎn)程方法的調(diào)用和數(shù)據(jù)的傳輸了。

如上,通過(guò)這種模式,可以解決一定問(wèn)題,但是對(duì)于Service來(lái)說(shuō),實(shí)在是太大才小用了,Service的專長(zhǎng),不是在數(shù)據(jù),還是在邏輯。對(duì)于傳數(shù)據(jù)而
言,Service還是重量了一點(diǎn),不但是有連接耗精力,傳輸經(jīng)由IPC,寫起來(lái)也夠費(fèi)勁。而且作為組件,Service隨時(shí)可能死掉,你還是要費(fèi)勁心機(jī)
的處理數(shù)據(jù)的持久化,得不償失。
利用Application傳輸
好吧,如果你需要在不同頁(yè)面之間共有某個(gè)內(nèi)存對(duì)象,很合適的一種方式是把它們?nèi)拥?strong>Application里面。Application是Context的一個(gè)子類,它會(huì)在整個(gè)應(yīng)用任何一個(gè)組件起來(lái)之前,先起來(lái)噓噓。它的生命周期會(huì)貫穿整個(gè)應(yīng)用所有組件的生命旅途,因此,放在其中的對(duì)象,不會(huì)被處理掉。
在Activity中,可以通過(guò)getApplication接口,隨時(shí)獲得Application對(duì)象的引用,用于實(shí)現(xiàn)一些全局對(duì)象的存儲(chǔ),和處理,真是最合適不過(guò)的地方了。

當(dāng)然,好東西也不要使用過(guò)度,可以想象,由于Application存活周期長(zhǎng),其上引用的對(duì)象一直缺少被釋放的機(jī)會(huì),如果你把它當(dāng)成垃圾場(chǎng),什么東西都往里扔,污染環(huán)境,混亂邏輯不提,單就是濫用內(nèi)存資源這一項(xiàng),就夠罪孽深重一把了。
因此,如果數(shù)據(jù)不是真的需要全局使用,不要擱在其中,如果數(shù)據(jù)太大,不要全部load出來(lái),合理使用數(shù)據(jù)庫(kù)等外存儲(chǔ)設(shè)備,還是必須要的。
結(jié)語(yǔ)
還有一些特殊情況,可以考慮用一些特殊的方式。比如子Activity之間,可以通過(guò)調(diào)用getParent獲得父Activity的引用,來(lái)訪問(wèn)期間的對(duì)象,云云。小眾情況,姑且不提。
以上這些概念,我相信所有的coder都了如指掌,如何處理這樣的數(shù)據(jù),都心如明鏡。我只是給它們套上了一件Android的外衣,讓初入Android的coder們,能迅速找到心儀的兵器,劈山砍石,攻城拔寨。
|