Android官方架構(gòu)組件指南
來源:原創(chuàng) 時(shí)間:2017-10-18 瀏覽:0 次此攻略適用于那些從前或現(xiàn)在進(jìn)行Android運(yùn)用的根底開發(fā),并期望了解和學(xué)習(xí)編寫Android程序的最佳實(shí)踐和架構(gòu)。經(jīng)過學(xué)習(xí)來構(gòu)建強(qiáng)壯的出產(chǎn)等級(jí)的運(yùn)用。
留意:此攻略默許你對(duì)Android開發(fā)有比較深的了解,熟知Android Framework。如果你還僅僅個(gè)Android開發(fā)新手,那么主張先學(xué)習(xí)下Android的根底知識(shí)。
?
Android程序員面對(duì)的問題
傳統(tǒng)的桌面運(yùn)用程序開發(fā)在大多數(shù)狀況下,發(fā)動(dòng)器快捷辦法都有一個(gè)進(jìn)口點(diǎn),并作為一個(gè)單一的進(jìn)程運(yùn)轉(zhuǎn),但Android運(yùn)用程序的結(jié)構(gòu)更為雜亂。典型的Android運(yùn)用程序由多個(gè)運(yùn)用程序組件構(gòu)成,包含Activity,F(xiàn)ragment,Service,ContentProvider和Broadcast Receiver。
大多數(shù)這些運(yùn)用程序組件在Android操作體系運(yùn)用的AndroidManifest中聲明,以決議怎樣將運(yùn)用程序集成到設(shè)備上來為用戶供給完好的體會(huì)。盡管如前所述,桌面運(yùn)用程序傳統(tǒng)上是作為一個(gè)單一的進(jìn)程運(yùn)轉(zhuǎn)的,但正確編寫的Android運(yùn)用程序則需求更靈敏,由于用戶經(jīng)過設(shè)備上的不同運(yùn)用程序織造辦法,不斷切換流程和使命。
舉個(gè)比方,當(dāng)用戶在交際App上計(jì)劃共享一張相片,那么Android體系就會(huì)為此發(fā)動(dòng)相機(jī)來完結(jié)此次懇求。此刻用戶脫離了交際App,可是這個(gè)用戶體會(huì)是無縫銜接的。相機(jī)可能又會(huì)觸發(fā)并發(fā)動(dòng)文件辦理器來挑選相片。終究回到交際App并共享相片。此外,在此進(jìn)程中的任何時(shí)候,用戶可能會(huì)被打電話中止,并在完結(jié)電話后再回來共享相片。
在Android中,這種運(yùn)用間跳轉(zhuǎn)行為很常見,因而你的運(yùn)用有必要正確處理這些流程。請(qǐng)記住,移動(dòng)設(shè)備是資源有限的,所以在任何時(shí)候,操作體系可能需求殺死一些運(yùn)用來為新的運(yùn)用騰出空間。
你的運(yùn)用程序的一切組件都能夠被獨(dú)自發(fā)動(dòng)或無序發(fā)動(dòng),而且在任何時(shí)候由用戶或體系毀掉。由于運(yùn)用程序組件是時(shí)刻短的,它們的生命周期(創(chuàng)立和毀掉時(shí))不受你的操控,因而你不該該將任何運(yùn)用程序數(shù)據(jù)或狀況存儲(chǔ)在運(yùn)用程序組件中,而且運(yùn)用程序組件不該彼此依靠。
常見的架構(gòu)原理
如果你無法運(yùn)用運(yùn)用程序組件來存儲(chǔ)運(yùn)用程序數(shù)據(jù)和狀況,應(yīng)怎樣構(gòu)建運(yùn)用程序?
在你的App開發(fā)中你應(yīng)該將重心放在分層上,如果將一切的代碼都寫在Activity或許Fragment中,那問題就大了。任何不是處理UI或跟操作體系交互的操作不該該放在這兩個(gè)類中。盡量堅(jiān)持它們代碼的精簡(jiǎn),這樣你能夠防止許多與生命周期相關(guān)的問題。記住你并不能掌控Activity和Fragment,他們僅僅在你的App和Android體系間起了橋梁的效果。任何時(shí)候,Android體系可能會(huì)依據(jù)用戶操作或其他要素(如低內(nèi)存)來收回它們。最好盡量削減對(duì)他們的依靠,以供給堅(jiān)實(shí)的用戶體會(huì)。
還有一點(diǎn)比較重要的就是耐久模型驅(qū)動(dòng)UI。運(yùn)用耐久模型首要是由于當(dāng)你的UI被收回或許在沒有網(wǎng)絡(luò)的狀況下還能正常給用戶展現(xiàn)數(shù)據(jù)。模型是用來處理運(yùn)用數(shù)據(jù)的組件,它們獨(dú)立于運(yùn)用中的視圖和四大組件。因而模型的生命周期必定和UI是別離的。堅(jiān)持UI代碼的整齊,會(huì)讓你能更簡(jiǎn)略的辦理和調(diào)整UI。讓你的運(yùn)用依據(jù)模型開發(fā)能夠很好的辦理你運(yùn)用的數(shù)據(jù)并是你的運(yùn)用更具測(cè)驗(yàn)性和持續(xù)性。
運(yùn)用架構(gòu)引薦
回到這篇文章的主題,來說說Android官方架構(gòu)組件(一下簡(jiǎn)稱架構(gòu))。一下會(huì)介紹怎樣在你的運(yùn)用中實(shí)踐這一架構(gòu)形式。
留意:不可能存在某一種架構(gòu)辦法能夠完美合適任何場(chǎng)景。話雖如此,這種架構(gòu)應(yīng)該是大多數(shù)用例的杰出起點(diǎn)。如果你現(xiàn)已有了很好的Android運(yùn)用程序架構(gòu)辦法,請(qǐng)持續(xù)堅(jiān)持。
假定我們需求一個(gè)實(shí)際用戶材料的UI,該用戶的材料文件將運(yùn)用REST API從效勞端獲取。
構(gòu)建用戶界面
我們的這個(gè)用戶界面由一個(gè)UserProfileFragment.java文件和它的布局文件user_profile_layout.xml。
為了驅(qū)動(dòng)UI,數(shù)據(jù)模型需求持有下面兩個(gè)數(shù)據(jù):
User ID:用戶的標(biāo)識(shí)符。最好運(yùn)用Fragment的參數(shù)將此信息傳遞到Fragment中。如果Android操作體系收回了Fragment,則會(huì)保存此信息,以便下次從頭發(fā)動(dòng)運(yùn)用時(shí),該ID可用。
User Object:傳統(tǒng)的Java目標(biāo),代表用戶的數(shù)據(jù)。
為此,我們新建一個(gè)承繼自ViewModel的名為UserProfileViewModel的模型來持有這個(gè)數(shù)據(jù)。
ViewModel供給特定UI組件的數(shù)據(jù),例如Activity和Fragment,并處理與數(shù)據(jù)處理事務(wù)部分的通訊,例如調(diào)用其他組件來加載數(shù)據(jù)或轉(zhuǎn)發(fā)用戶修正。ViewModel不了解View,而且不受UI的重建(如重由于旋轉(zhuǎn)而導(dǎo)致的Activity的重建)的影響。
現(xiàn)在我們有一下三個(gè)文件:
user_profile.xml: 視圖的布局文件。
UserProfileViewModel.java: 持有UI數(shù)據(jù)的模型。
UserProfileFragment.java: 用于顯現(xiàn)數(shù)據(jù)模型中的數(shù)據(jù)并和用戶進(jìn)行交互。
一下是詳細(xì)代碼(為了簡(jiǎn)化,布局文件省掉)。
留意:上面的UserProfileFragment承繼自LifeCycleFragment而不是Fragment。當(dāng)Lifecycle的Api安穩(wěn)后,F(xiàn)ragment會(huì)默許完結(jié)LifeCycleOwner。
現(xiàn)在,我們有三個(gè)文件,我們?cè)鯓鱼暯铀鼈??究竟,?dāng)ViewModel的用戶字段被設(shè)置時(shí),我們需求一種通知UI的辦法。這兒就要說到LiveData了。
LiveData是一個(gè)可調(diào)查的數(shù)據(jù)持有者。它答應(yīng)運(yùn)用程序中的組件調(diào)查L(zhǎng)iveData目標(biāo)持有的數(shù)據(jù),而不會(huì)在它們之間創(chuàng)立顯式和剛性的依靠途徑。LiveData還尊重你的運(yùn)用程序組件(Activity,F(xiàn)ragment,Service)的生命周期狀況,并做正確的作業(yè)以防止內(nèi)存走漏,然后你的運(yùn)用程序不會(huì)耗費(fèi)更多的內(nèi)存。
如果你現(xiàn)已運(yùn)用了想Rxjava活著Agrea這類第三方庫(kù),那么你能夠運(yùn)用它們替代LiveData,不過你需求處理好它們與組件生命周期之間的聯(lián)系。
現(xiàn)在我們運(yùn)用LiveData來替代UserProfileViewModel中的User字段。所以Fragment能夠經(jīng)過調(diào)查它來更新數(shù)據(jù)。LiveData值得稱道的當(dāng)?shù)鼐驮谟谒巧芷诟兄?,?dāng)生命周期完畢是,其上的調(diào)查者會(huì)被即便整理。
然后將UserProfileFragment修正如下,調(diào)查數(shù)據(jù)并更新UI:
一旦用戶數(shù)據(jù)更新,onChanged回調(diào)將被調(diào)用然后UI會(huì)被改寫。
如果你了解一些運(yùn)用調(diào)查者形式第三方庫(kù),你會(huì)覺得古怪,為什么沒有在Fragment的onStop()辦法中將調(diào)查者移除。關(guān)于LiveData來說這是沒有必要的,由于它是生命周期感知的,這意味著如果UI處于不活動(dòng)狀況,它就不會(huì)調(diào)用調(diào)查者的回調(diào)來更新數(shù)據(jù)。而且在onDestroy后會(huì)主動(dòng)移除。
我們也不需求處理任何視圖重建(如屏幕旋轉(zhuǎn))。ViewModel會(huì)主動(dòng)康復(fù)重建前的數(shù)據(jù)。當(dāng)新的視圖被創(chuàng)立出來后,它會(huì)接收到與之前相同的ViewModel實(shí)例,而且調(diào)查者的回調(diào)會(huì)被馬上調(diào)用,更新最新的數(shù)據(jù)。這也是ViewModel為什么不能直接引證視圖目標(biāo),由于它的生命周期善于視圖目標(biāo)。
獲取數(shù)據(jù)
現(xiàn)在我們將視圖和模型銜接起來,可是模型該怎樣獲取數(shù)據(jù)呢?在這個(gè)比方中,我們假定運(yùn)用REST API從后臺(tái)獲取。我們將運(yùn)用Retrofit來向后臺(tái)懇求數(shù)據(jù)。
我們的retrofit類Webservice如下:
如果僅僅簡(jiǎn)略的完結(jié),ViewModel能夠直接操作Webservice來獲取用戶數(shù)據(jù)。盡管這樣能夠正常作業(yè),但你的運(yùn)用無法確保它的后續(xù)迭代。由于這樣做將太多的職責(zé)讓ViewModel來承當(dāng),這樣就違反類之前講到的分層準(zhǔn)則。又由于ViewModel的生命周期是綁定在Activity和Fragment上的,所以當(dāng)UI被毀掉后如果丟掉一切數(shù)據(jù)將是很差的用戶體會(huì)。所以我們的ViewModel將和一個(gè)新的模塊進(jìn)行交互,這個(gè)模塊叫Repository。
Repository模塊擔(dān)任處理數(shù)據(jù)。它為運(yùn)用程序的其余部分供給了一個(gè)潔凈的API。他知道在數(shù)據(jù)更新時(shí)從哪里獲取數(shù)據(jù)和調(diào)用哪些API調(diào)用。你能夠?qū)⑺鼈円暈椴煌瑪?shù)據(jù)源(耐久性模型,Web效勞,緩存等)之間的中介者。
UserRepository類如下:
盡管repository模塊看上去沒有必要,但他起著重要的效果。它為App的其他部分籠統(tǒng)出了數(shù)據(jù)源?,F(xiàn)在我們的ViewModel并不知道數(shù)據(jù)是經(jīng)過WebService來獲取的,這意味著我們能夠隨意替換掉獲取數(shù)據(jù)的完結(jié)。
辦理組件間的依靠聯(lián)系
上面這種寫法能夠看出來UserRepository需求初始化Webservice實(shí)例,這盡管說起來簡(jiǎn)略,但要完結(jié)的話還需求知道Webservice的詳細(xì)結(jié)構(gòu)辦法該怎樣寫。這將加大代碼的雜亂度,別的UserRepository可能并不是僅有運(yùn)用Webservice的目標(biāo),所以這種在內(nèi)部構(gòu)建Webservice實(shí)例顯然是不引薦的,下面有兩種形式來處理這個(gè)問題:
依靠注入:依靠注入答應(yīng)類界說它們的依靠聯(lián)系而不結(jié)構(gòu)它們。在運(yùn)轉(zhuǎn)時(shí),另一個(gè)類擔(dān)任供給這些依靠聯(lián)系。我們主張?jiān)贏ndroid運(yùn)用程序中運(yùn)用Google的Dagger 2庫(kù)完結(jié)依靠注入。Dagger 2經(jīng)過遍歷依靠聯(lián)系樹主動(dòng)構(gòu)建目標(biāo),并在依靠聯(lián)系上供給編譯時(shí)確保。
效勞定位器:效勞定位器供給了一個(gè)注冊(cè)表,其間類能夠獲取它們的依靠聯(lián)系而不是結(jié)構(gòu)它們。與依靠注入(DI)比較,完結(jié)起來相對(duì)簡(jiǎn)略,因而如果您不了解DI,請(qǐng)改用Service Locator。
這些形式答應(yīng)你擴(kuò)展代碼,由于它們供給清晰的形式來辦理依靠聯(lián)系,而不會(huì)重復(fù)代碼或增加雜亂性。兩者都答應(yīng)交流完結(jié)進(jìn)行測(cè)驗(yàn);這是運(yùn)用它們的首要優(yōu)點(diǎn)之一。在這個(gè)比方中,我們將運(yùn)用Dagger 2來辦理依靠聯(lián)系。
銜接ViewModel和Repository
現(xiàn)在,我們的UserProfileViewModel能夠改寫成這樣:
緩存數(shù)據(jù)
上面的Repository盡管網(wǎng)絡(luò)懇求做了封裝,可是它依靠后臺(tái)數(shù)據(jù)源,所以存在缺乏。
上面的UserRepository完結(jié)的問題是,在獲取數(shù)據(jù)之后,它不會(huì)保存在任何當(dāng)?shù)亍H绻脩裘撾xUserProfileFragment偏從頭進(jìn)來,則運(yùn)用程序?qū)念^獲取數(shù)據(jù)。這是欠好的,有兩個(gè)原因:它糟蹋了名貴的網(wǎng)絡(luò)帶寬和迫運(yùn)用戶等候新的查詢完結(jié)。為了處理這個(gè)問題,我們將向我們的UserRepository增加一個(gè)新的數(shù)據(jù)源,它將把User目標(biāo)緩存在內(nèi)存中。如下:
耐久化數(shù)據(jù)
在當(dāng)時(shí)的完結(jié)中,如果用戶旋轉(zhuǎn)屏幕或脫離并回來到運(yùn)用程序,現(xiàn)有UI將當(dāng)即可見,由于Repository會(huì)從內(nèi)存中檢索數(shù)據(jù)??墒?,如果用戶脫離運(yùn)用程序,并在Android操作體系殺死進(jìn)程后幾小時(shí)后又會(huì)怎樣樣?
在現(xiàn)在的完結(jié)中,我們將需求從網(wǎng)絡(luò)中再次獲取數(shù)據(jù)。這不僅是一個(gè)糟糕的用戶體會(huì),也是糟蹋,由于它將運(yùn)用移動(dòng)數(shù)據(jù)來從頭獲取相同的數(shù)據(jù)。你以經(jīng)過緩存Web懇求來簡(jiǎn)略地處理這個(gè)問題,但它會(huì)發(fā)作新的問題。如果懇求一個(gè)朋友列表而不是單個(gè)用戶,會(huì)發(fā)作什么狀況?那么你的運(yùn)用程序可能會(huì)顯現(xiàn)不共同的數(shù)據(jù),這是最令人困惑的用戶體會(huì)。例如,相同的用戶的數(shù)據(jù)可能會(huì)不同,由于朋友列表懇求和用戶懇求能夠在不同的時(shí)刻履行。你的運(yùn)用需求兼并他們,以防止顯現(xiàn)不共同的數(shù)據(jù)。
正確的處理辦法是運(yùn)用耐久模型。這時(shí)候Room就派上用場(chǎng)了。
Room是一個(gè)目標(biāo)映射庫(kù),它供給本地?cái)?shù)據(jù)耐久性和最少的樣板代碼。在編譯時(shí),它依據(jù)形式驗(yàn)證每個(gè)查詢,然后過錯(cuò)的SQL查詢會(huì)導(dǎo)致編譯時(shí)過錯(cuò),而不是運(yùn)轉(zhuǎn)時(shí)失利。Room籠統(tǒng)了運(yùn)用原始SQL表和查詢的一些根本完結(jié)細(xì)節(jié)。它還答應(yīng)調(diào)查數(shù)據(jù)庫(kù)數(shù)據(jù)(包含調(diào)集和銜接查詢)的更改,經(jīng)過LiveData目標(biāo)揭露這些更改。
要運(yùn)用Room我們首要需求運(yùn)用@Entity來界說實(shí)體:
接著創(chuàng)立數(shù)據(jù)庫(kù)類:
值得留意的是MyDatabase是一個(gè)籠統(tǒng)了,Room會(huì)在編譯期間供給它的一個(gè)完結(jié)類。
接下來需求界說DAO:
接著在MyDatabase中增加獲取上面這個(gè)DAO的辦法:
這兒的load辦法回來的是LiveData
現(xiàn)在我們能夠修正UserRepository了:
這兒盡管我們將UserRepository的直接數(shù)據(jù)來歷從Webservice改為本地?cái)?shù)據(jù)庫(kù),但我們卻不需求修正UserProfileViewModel或許UserProfileFragment。這就是籠統(tǒng)層帶來的優(yōu)點(diǎn)。這也給測(cè)驗(yàn)帶來了便利,由于你能夠供給一個(gè)虛偽的UserRepository來測(cè)驗(yàn)?zāi)愕腢serProfileViewModel。
現(xiàn)在,如果用戶從頭回到這個(gè)界面,他們會(huì)馬上看到數(shù)據(jù),由于我們現(xiàn)已將數(shù)據(jù)做了耐久化的保存。當(dāng)然如果有用例需求,我們也可不展現(xiàn)太老舊的耐久化數(shù)據(jù)。
在一些用例中,比方下拉改寫,如果正處于網(wǎng)絡(luò)懇求中,那UI需求通知用戶正處于網(wǎng)絡(luò)懇求中。一個(gè)好的實(shí)踐辦法就是將UI與數(shù)據(jù)別離,由于UI可能由于各種原因被更新。從UI的視點(diǎn)來說,懇求中的數(shù)據(jù)和本地?cái)?shù)據(jù)相似,僅僅它還沒有被耐久化到數(shù)據(jù)庫(kù)中。
以下有兩種處理辦法:
將getUser的回來值中加入網(wǎng)絡(luò)狀況。
在Repository中供給一個(gè)能夠回來改寫狀況的辦法。如果你僅僅想在用戶經(jīng)過下拉改寫來通知用戶現(xiàn)在的網(wǎng)絡(luò)狀況的話,那這個(gè)辦法是比較合適的。
數(shù)據(jù)僅有來歷
在以上實(shí)例中,數(shù)據(jù)僅有來歷是數(shù)據(jù)庫(kù),這樣做的優(yōu)點(diǎn)是用戶能夠依據(jù)安穩(wěn)的數(shù)據(jù)庫(kù)數(shù)據(jù)來更新頁(yè)面,而不需求處理許多的網(wǎng)絡(luò)懇求狀況。數(shù)據(jù)庫(kù)有數(shù)據(jù)則運(yùn)用,沒有數(shù)據(jù)則等候其更新。
測(cè)驗(yàn)
我們之前說到分層能夠個(gè)運(yùn)用供給杰出的測(cè)驗(yàn)才能,接下來就看看我們?cè)鯓訙y(cè)驗(yàn)不同的模塊。
用戶界面與交互:這是僅有一個(gè)需求運(yùn)用到Android UI Instrumentation test的測(cè)驗(yàn)?zāi)K。測(cè)驗(yàn)UI的最好辦法就是運(yùn)用Espresso結(jié)構(gòu)。你能夠創(chuàng)立Fragment然后供給一個(gè)虛偽的ViewModel。由于Fragment只跟ViewModel交互,所以虛擬一個(gè)ViewModel就足夠了。
ViewModel:ViewModel能夠用JUnit test進(jìn)行測(cè)驗(yàn)。由于其不觸及界面與交互。而且你只需求虛擬UserRepository即可。
UserRepository:測(cè)驗(yàn)UserRepository相同運(yùn)用JUnit test。你能夠虛擬出Webservice和DAO。你能夠經(jīng)過運(yùn)用正確的網(wǎng)絡(luò)懇求來懇求數(shù)據(jù),讓后將數(shù)據(jù)經(jīng)過DAO寫入數(shù)據(jù)庫(kù)。如果數(shù)據(jù)庫(kù)中有相關(guān)數(shù)據(jù)則無需進(jìn)行網(wǎng)絡(luò)懇求。
UserDao:關(guān)于DAO的測(cè)驗(yàn),引薦運(yùn)用instrumentation進(jìn)行測(cè)驗(yàn)。由于此處無需UI,而且能夠運(yùn)用in-memory數(shù)據(jù)庫(kù)來確保測(cè)驗(yàn)的封閉性,不會(huì)影響到磁盤上的數(shù)據(jù)庫(kù)。
Webservice:堅(jiān)持測(cè)驗(yàn)的封閉性是適當(dāng)重要的,因而即便是你的Webservice測(cè)驗(yàn)也應(yīng)防止對(duì)后端進(jìn)行網(wǎng)絡(luò)呼叫。有許多第三方庫(kù)供給這方面的支撐。例如,MockWebServer是一個(gè)很棒的庫(kù),能夠協(xié)助你為你的測(cè)驗(yàn)創(chuàng)立一個(gè)假的本地效勞器。
架構(gòu)圖
輔導(dǎo)準(zhǔn)則
編程是一個(gè)構(gòu)思范疇,構(gòu)建Android運(yùn)用程序也不破例。有多種辦法來處理問題,無論是在多個(gè)Activity或Fragment之間傳遞數(shù)據(jù),仍是檢索長(zhǎng)途數(shù)據(jù)并將其在本地堅(jiān)持離線形式,或許是任何其他常見的場(chǎng)景。
盡管以下主張不是強(qiáng)制性的,但經(jīng)歷通知我們,遵從這些主張將使你的代碼庫(kù)從長(zhǎng)遠(yuǎn)來看愈加強(qiáng)壯,可測(cè)驗(yàn)和可保護(hù)。
在AndroidManifest中界說的Activity,Service,Broadcast Receiver等,它們不是數(shù)據(jù)源。相反,他們僅僅用于協(xié)諧和展現(xiàn)數(shù)據(jù)。由于每個(gè)運(yùn)用程序組件的壽數(shù)適當(dāng)短,運(yùn)轉(zhuǎn)狀況取決于用戶與其設(shè)備的交互以及運(yùn)轉(zhuǎn)時(shí)的全體當(dāng)時(shí)運(yùn)轉(zhuǎn)狀況,所以不要將這些組件作為數(shù)據(jù)源。
你需求在運(yùn)用程序的各個(gè)模塊之間創(chuàng)立清晰界定的職責(zé)規(guī)模。例如,不要在不同的類或包之間傳遞用于加載網(wǎng)絡(luò)數(shù)據(jù)的代碼。相同,不要將數(shù)據(jù)緩存和數(shù)據(jù)綁定這兩個(gè)職責(zé)徹底不同的放在同一個(gè)類中。
每個(gè)模塊之間要竟可能少的彼此露出。不要抱有僥幸心理去揭露一個(gè)關(guān)于模塊的內(nèi)部完結(jié)細(xì)節(jié)的接口。你可能會(huì)在短期內(nèi)獲得到快捷,可是跟著代碼庫(kù)的開展,你將多付屢次技術(shù)性債款。
當(dāng)你界說模塊之間的交互時(shí),請(qǐng)考慮怎樣使每個(gè)模塊阻隔。例如,具有用于從網(wǎng)絡(luò)中提取數(shù)據(jù)的界說杰出的API將使得更簡(jiǎn)略測(cè)驗(yàn)在本地?cái)?shù)據(jù)庫(kù)中耐久存在該數(shù)據(jù)的模塊。相反,如果將這兩個(gè)模塊的邏輯組合在一同,或許將整個(gè)代碼庫(kù)中的網(wǎng)絡(luò)代碼放在一同,那么測(cè)驗(yàn)就更難(如果不是不可能)。
你的運(yùn)用程序的中心是什么讓它獨(dú)立出來。不要花時(shí)刻重復(fù)輪子或一次又一次地編寫相同的樣板代碼。相反,將精力會(huì)集在使你的運(yùn)用程序絕無僅有的一起,讓Android架構(gòu)組件和其他引薦的庫(kù)來處理重復(fù)的樣板代碼。
堅(jiān)持盡可能多的相關(guān)聯(lián)的新鮮數(shù)據(jù),以便你的運(yùn)用程序在設(shè)備處于脫機(jī)形式時(shí)可用。盡管你能夠享用穩(wěn)定和高速銜接,但你的用戶可能不會(huì)。
你的Repository應(yīng)指定一個(gè)數(shù)據(jù)源作為實(shí)在的單一來歷。每逢你的運(yùn)用程序需求拜訪這些數(shù)據(jù)時(shí),它應(yīng)該一直源于實(shí)在的單一來歷。
擴(kuò)展: 揭露網(wǎng)絡(luò)狀況
在上面的小結(jié)我們成心省掉了網(wǎng)絡(luò)過錯(cuò)和加載狀況來確保比方的簡(jiǎn)潔性。在這一小結(jié)我們演示一種運(yùn)用Resource類來封裝數(shù)據(jù)及其狀況。以此來揭露網(wǎng)絡(luò)狀況。
下面是簡(jiǎn)略的Resource完結(jié):
認(rèn)為從網(wǎng)絡(luò)上抓取視頻的一起在UI上顯現(xiàn)數(shù)據(jù)庫(kù)的舊數(shù)據(jù)是很常見的用例,所以我們要?jiǎng)?chuàng)立一個(gè)能夠在多個(gè)當(dāng)?shù)刂貜?fù)運(yùn)用的協(xié)助類NetworkBoundResource。以下是NetworkBoundResource的決策樹:
NetworkBoundResource從調(diào)查數(shù)據(jù)庫(kù)開端,當(dāng)?shù)谝淮螐臄?shù)據(jù)庫(kù)加載完實(shí)體后,NetworkBoundResource會(huì)查看這個(gè)效果是否滿意用來展現(xiàn)的需求,如不滿意則需求從網(wǎng)上從頭獲取。當(dāng)然以上兩種狀況可能一起發(fā)作,你期望先將數(shù)據(jù)顯現(xiàn)在UI上的一起去網(wǎng)絡(luò)上懇求新數(shù)據(jù)。
如果網(wǎng)絡(luò)懇求效果,則將效果保存到數(shù)據(jù)庫(kù),然后從頭從數(shù)據(jù)庫(kù)加載數(shù)據(jù),如果網(wǎng)絡(luò)懇求失利,則直接傳遞過錯(cuò)信息。
留意:在上面的進(jìn)程中能夠看到當(dāng)將新數(shù)據(jù)保存到數(shù)據(jù)庫(kù)后,我們從頭從數(shù)據(jù)庫(kù)加載數(shù)據(jù)。盡管大部分狀況我們不用如此,由于數(shù)據(jù)庫(kù)會(huì)為我們傳遞此次更新。但另一方面,依靠數(shù)據(jù)庫(kù)內(nèi)部的更新機(jī)制并不是我們想要的如果更新的數(shù)據(jù)與舊數(shù)據(jù)共同,則數(shù)據(jù)谷不會(huì)做出更新提示。我們也不期望直接從網(wǎng)絡(luò)懇求中獲取數(shù)據(jù)直接用于UI,由于這樣違反了單一數(shù)據(jù)源的準(zhǔn)則。
下面是NetworkBoundResource類的公共api:
留意到上面界說了兩種泛型,ResultType和RequestType,由于從網(wǎng)絡(luò)懇求回來的數(shù)據(jù)類型可能會(huì)和數(shù)據(jù)庫(kù)回來的不共同。
別的留意到上面代碼中的ApiResponse這個(gè)類,他是將Retroft2.Call轉(zhuǎn)換成LiveData的一個(gè)簡(jiǎn)略封裝。
下面是NetworkBoundResource余下部分的完結(jié):
接著我們就能夠在UserRepository中運(yùn)用NetworkBoundResource了。