Android Jetpack介紹
Android Jetpack 是一套組件、工具和指導(dǎo),可以幫助您快速構(gòu)建出色的 Android 應(yīng)用场靴。
Google在17年的I/O大會上推出了架構(gòu)組件(Architecture Component)队丝。
隨后在18年I/O大會上發(fā)布了 Android Jetpack,Jetpack 是Android開發(fā)組件工具集腹缩,旨在幫助我們輕松構(gòu)建更穩(wěn)定屿聋、更健壯空扎、以及更可維護(hù)的應(yīng)用程序。
-
緊接著Google推出AndroidX润讥,將許多Google認(rèn)為是正確的方案和實(shí)踐集中起來了转锈。
AndroidX 是對support library的重大改進(jìn)。
AndroidX中的所有軟件包名都以字符串a(chǎn)ndroidx.開頭楚殿,位于一致的命名空間中撮慨。
與support支持庫不同,AndroidX中各個(gè)組件可單獨(dú)維護(hù)和更新脆粥。
所有新的支持庫開發(fā)都將在AndroidX庫中進(jìn)行甫煞。
目前很多組件庫新版本都遷移到了androidx。比如Lifecycle2.0.0+冠绢、Paging2.0.0+抚吠、ViewPager2等,非官方庫也積極響應(yīng)弟胀,比如lottie2.8.0+版本之后就使用了androidx實(shí)現(xiàn)楷力。由此可以得出,官方在已有組件新版本實(shí)現(xiàn)和全新組件的開發(fā)都將只支持androidx孵户,所以盡快將自己的項(xiàng)目遷移到androidx吧萧朝。
- Jetpack結(jié)構(gòu)圖(2018年版本),目前圖中大部分組件都推出了穩(wěn)定的release版本夏哭。
Jetpack主要分為4個(gè)部分检柬,基礎(chǔ)、架構(gòu)竖配、行為何址、界面。從圖中得知进胯,Jetpack并不全是些新東西用爪,只要是能夠幫助開發(fā)者更好更方便地構(gòu)建應(yīng)用程序的組件,Google都將其歸納入了Jetpack胁镐,可以看出Google對jetpack很重視偎血,對開發(fā)者很上心。
剛剛結(jié)束的Google I/O 2019大會上盯漂,Jetpack又迎來了新組件CameraX
颇玷、SavedStateViewModel
、Jetpack Compose
等等就缆,提出Kotlin first 帖渠,并強(qiáng)調(diào)大部分新的Jetpack Apis和功能將會優(yōu)先提供 Kotlin 版本。參考文章
每個(gè) Jetpack 組件均可單獨(dú)采用违崇,并且它們可以流暢地協(xié)作阿弃。Android開發(fā)請一定關(guān)注和使用Jetpack+Kotlin诊霹。
本文重點(diǎn)是在架構(gòu)組件的使用上,我們先來看看官方推薦的架構(gòu)實(shí)現(xiàn)渣淳。
官方推薦架構(gòu)
使用此架構(gòu)能帶來什么好處脾还?
- UI和業(yè)務(wù)邏輯解耦。
- 有效避免生命周期組件內(nèi)存泄漏入愧。
- 提高模塊可測試性鄙漏。
- 提高應(yīng)用穩(wěn)定性,有效降低以下異常發(fā)生概率棺蛛。
- Can not perform this action after onSaveInstanceState
- WindowManager$BadTokenException, is your activity running?
- OOM 怔蚌、 NullPointerException
- ...
測試每個(gè)組件
界面和交互:使用 Android 界面插樁測試∨陨蓿基于此架構(gòu)只需mock 一個(gè)
ViewModel
即可完成界面測試桦踊。ViewModel:使用 JUnit 測試。只需mock一個(gè)類终畅,即
Repository
籍胯。Repository:使用 JUnit 測試。只需mock兩個(gè)類离福,XxxDao杖狼,XxxService;由于XxxDao妖爷,XxxService都是接口蝶涩,還可以創(chuàng)建虛擬實(shí)現(xiàn)來完成復(fù)雜測試用例。
XxxDao:可以使用插樁測試來測試 DAO 類絮识。這里注意對于每個(gè)測試绿聘,都請創(chuàng)建內(nèi)存中數(shù)據(jù)庫以確保測試沒有任何副作用(例如更改磁盤上的數(shù)據(jù)庫文件)。
XxxService:就Retrofit而言可以使用MockWebServer模擬本地服務(wù)器笋除。
評價(jià)一個(gè)架構(gòu)好不好主要看三點(diǎn):穩(wěn)定性斜友、易維護(hù)、可測試程度垃它。
提到架構(gòu)組件庫,不得不說的是Lifecycle
庫烹看。文章后面部分就分別從該庫中的Lifecycle国拇、ViewModel、LiveData這三個(gè)類來簡要分析其實(shí)現(xiàn)原理以及使用建議惯殊。
Lifecycle
Lifecycle是一個(gè)類酱吝,它包含組件(Activity或Fragment)生命周期狀態(tài)的信息,并允許其他對象觀察此狀態(tài)土思。
那lifecycle是如何跟蹤組件生命周期的呢务热?
上圖中states表示組件狀態(tài)忆嗜,events表示組件生命周期事件。其實(shí)Lifecycle代碼內(nèi)部使用了兩個(gè)主要枚舉(Event崎岂、State)來跟蹤其關(guān)聯(lián)組件的生命周期狀態(tài)捆毫。
- 其中枚舉Event的值和Activity或Fragment組件的生命周期回調(diào)事件一一對應(yīng)。
-
而枚舉State則表示被跟蹤組件的當(dāng)前狀態(tài)冲甘,其中
STARTED
和RESUMED
為活躍狀態(tài)绩卤,配合LiveData使用時(shí),只有組件處于活躍狀態(tài)才能接受到數(shù)據(jù)更新通知江醇。
實(shí)踐示例:工具類LifecycleHandler濒憋,一個(gè)具有生命周期感知的Handler。
LifecycleOwner和LifecycleRegistry
- LifecycleOwner: 是一個(gè)單一的方法接口陶夜,表示該類具有生命周期凛驮。support包從26.1.0版本開始,F(xiàn)ragment和Activity默認(rèn)實(shí)現(xiàn)了該接口条辟,這樣直接和LiveData使用就能獲取組件的生命周期感知能力黔夭。
- LifecycleRegistry: Lifecycle接口的實(shí)現(xiàn)類,協(xié)助組件處理生命周期捂贿,可處理多個(gè)觀察者纠修。如果你想自定義LifecyclerOwner請參考support包中Fragment和Activity實(shí)現(xiàn)。
ViewModel
ViewModel 是用來保存應(yīng)用UI數(shù)據(jù)的類厂僧,它會在配置變更(Configuration Change)后繼續(xù)存在扣草。
先看看官方給出的ViewModel生命周期圖解
關(guān)于ViewModel的生命周期就一句話:在Activity、Fragment等組件整個(gè)生命周期過程中颜屠,ViewModel的實(shí)例有且只有一個(gè)辰妙。
這樣設(shè)計(jì)好處在哪呢?
可用ViewModel存儲數(shù)據(jù)甫窟,它能安全度過手機(jī)旋轉(zhuǎn)等配置變更場景密浑。
ViewModel能很好的實(shí)現(xiàn)多個(gè)Fragment之間的數(shù)據(jù)共享。
如果界面和業(yè)務(wù)都比較復(fù)雜粗井,ViewModel會不會爆掉尔破?對于這種場景,官方也給出了解決思路:單一責(zé)任原則浇衬。
上圖為官方中文視頻截圖懒构,從單一責(zé)任原則考慮提出了實(shí)現(xiàn)建議。
Actvity或Fragment只顯示UI和接收互動(dòng)耘擂。
為避免ViewModel臃腫胆剧,可創(chuàng)建presenter處理UI數(shù)據(jù)。
Repository 數(shù)據(jù)源操作入口醉冤。(便于單元測試)
還可配合其它架構(gòu)組件使用秩霍。
關(guān)于ViewModel的最佳實(shí)踐
如何時(shí)候都不要將Context傳入ViewModel篙悯。
如果要在ViewModel中使用Application實(shí)例,請使用AndroidViewModel類铃绒。
ViewModel+LiveData+Databinding 可構(gòu)建反應(yīng)式UI鸽照。(請查看文末提供的參考資料)
-
ViewModel與onSaveInstanceState要配合使用。
ViewModel onSaveInstanceState 能度過配置變更 能度過配置變更和進(jìn)程關(guān)閉 能存儲大量數(shù)據(jù) 只可存儲少量數(shù)據(jù) xx 必須序列化 其實(shí)ViewModel和onSaveInstanceState是相輔相成的匿垄,當(dāng)進(jìn)程被關(guān)閉時(shí)移宅,ViewModel會被銷毀,而onSaveInstanceState不會受影響椿疗,所以可用onSaveInstanceState存儲少量關(guān)鍵數(shù)據(jù)(如userId)漏峰,并在該場景恢復(fù)時(shí)用來加載頁面數(shù)據(jù)。
在使用ViewModel時(shí)届榄,如果頁面僅僅是簡單的展示數(shù)據(jù)沒什么交互浅乔,一個(gè)LiveData就能輕松搞定,但實(shí)際情況是大多數(shù)頁面復(fù)雜且交互多铝条,就想著怎樣更好的處理ViewModel和View之間的通信靖苇,直到看到了這篇文章,參考之后得出了下圖實(shí)現(xiàn)班缰。
ViewModel和View之間通信模型
- UserProfileActivity引用UserViewModel贤壁,可觀察其提供的UserLiveData、StatusLiveData埠忘、PageStateLiveData數(shù)據(jù)源變更分別處理數(shù)據(jù)顯示脾拆、頁面loading、跳轉(zhuǎn)等UI操作莹妒。
- 注意Activity和ViewModel之間是單向引用名船。為避免內(nèi)存泄漏,ViewModel不能持有任何Context引用旨怠。
該模型如何響應(yīng)用戶事件的渠驼?比如點(diǎn)擊某個(gè)按鈕,需要提交信息給server鉴腻,并在成功響應(yīng)后刷新UI迷扇,這個(gè)過程中ViewModel和View是如何通信的?這里簡單描述下該過程爽哎,首先是Activity將更新事件傳遞給ViewModel谋梭,ViewModel有將其委托給Presenter處理,Presenter將處理狀態(tài)和結(jié)果倦青,通過給圖中指定的LiveData設(shè)置數(shù)據(jù),liveData就能將新數(shù)據(jù)回調(diào)給Activity盹舞,這樣頁面上所有操作就都能通過數(shù)據(jù)來驅(qū)動(dòng)了产镐。
另外隘庄,如果Activity中存在多個(gè)View組件(Fragment),這些組件可直接依賴Activity的ViewModel進(jìn)行交互癣亚。
LiveData
LiveData是一個(gè)具有生命周期感知特性的可觀察的數(shù)據(jù)保持類丑掺。
- LiveData只通知活躍狀態(tài)(
STARTED
orRESUMED
)的Observer更新,并在DESTROYED
狀態(tài)時(shí)自動(dòng)移除Observers述雾,來避免內(nèi)存泄漏街州。 - 始終保持最新數(shù)據(jù)。舉例:1.退后臺的Activity在返回前臺后會立即收到最新數(shù)據(jù)玻孟。2. 配置變更導(dǎo)致Activity重建后也會立即收到最新數(shù)據(jù)唆缴。
- 共享資源。單利模式共享同一個(gè)LiveData黍翎。
-
SingleLiveEvent 只通知一次事件面徽,適用于Navigation event、SnackBar等等場景匣掸。
參考文章: SingleLiveEvent or EventWrapper
LiveData趟紊、MutableLiveData、MediatorLiveData三者關(guān)系?
- 繼承關(guān)系:MediatorLiveData -> MutableLiveData -> LiveData碰酝。 所以MediatorLiveData功能最強(qiáng)大霎匈。
- LiveData 是一個(gè)具有生命周期感知的可觀察的數(shù)據(jù)保持類。
- MutableLiveData 在LiveData基礎(chǔ)上打開了修改Value的方法權(quán)限送爸。
- MediatorLiveData 可管理多個(gè)LiveData铛嘱。
Transformations
用過RxJava的朋友都知道,它可以很方便地在Observable之間轉(zhuǎn)換碱璃。LiveData也提供了類似的功能弄痹。
-
map : 將一種數(shù)據(jù)類型的
LiveData<A>
轉(zhuǎn)換為另一種類型的LiveData<B>
// 示例代碼:觀察將被轉(zhuǎn)換LiveData<User>,待其數(shù)據(jù)源變更后轉(zhuǎn)換為LiveData<String>并通知訂閱者嵌器。 LiveData<User> userLiveData = ...; LiveData<String> userName = Transformations.map(userLiveData, user -> { user.name + " " + user.lastName });
-
switchMap : 和map類似肛真。差別在于triggerLiveData變更后,會觸發(fā)和等待另外一個(gè)LiveData獲取數(shù)據(jù)爽航。
// 示例代碼:將addressInputLiveData轉(zhuǎn)換為postalCodeLiveData. class MyViewModel extends ViewModel { private final PostalCodeRepository repository; private final MutableLiveData<String> addressInput = new MutableLiveData(); public final LiveData<String> postalCode = Transformations.switchMap(addressInput, (address) -> { return repository.getPostCode(address); }); public MyViewModel(PostalCodeRepository repository) { this.repository = repository } // addressInputLiveData變更時(shí)觸發(fā)repository.getPostCode蚓让, // 待其回去成功后,再將數(shù)據(jù)設(shè)置給postalCodeLiveData讥珍。 private void setInput(String address) { addressInput.setValue(address); } }
以上為使用示例代碼历极,其內(nèi)部使用的MediatorLiveData
實(shí)現(xiàn),較簡單衷佃,感興趣的朋友請自行查閱源碼趟卸。
幾個(gè)問題
LifecycleOwner組件是如何與liveData通信的?
- SupportActivity 通過添加一個(gè)空的ReportFragment來處理生命周期狀態(tài)變更回調(diào);Fragment則在自身生命周期函數(shù)中處理锄列。
- LifecycleOwner組件图云,通過LifecycleRegistry類中handleLifecycleEvent -> dispatchEvent方法與liveData通信,從而使liveData具有感知組件生命周期的能力邻邮。
- 組件銷毀時(shí)竣况,LifecycleRegistry會通知liveData移除observer。
ViewModel如何做到一直在內(nèi)存中筒严,直到Activity銷毀或Fragment被移除時(shí)才被清除的丹泉?
1.x.x版本實(shí)現(xiàn)
- Activity或Fragment會添加一個(gè)空的HolderFragment,而ViewModelStore實(shí)例被HolderFragment持有鸭蛙,所以就保證了整個(gè)生命周期中ViewModelStore實(shí)例始終唯一摹恨,也就保證了其緩存的ViewModel實(shí)例會一直存在直到組件銷毀(在onDestroy中會調(diào)用ViewModelStore.clear()方法清除其緩存的ViewModel實(shí)例)。
- 由于這個(gè)HolderFragment設(shè)置了setRetainInstance(true)规惰, 這樣在Activity重建時(shí)它不會執(zhí)行onDestroy回調(diào)睬塌,這就是它能度過配置變更的原因。
2.x.x版本-對應(yīng)androidx-fragment-v1.0.0
- Activity
- 緩存:在onRetainNonConfigurationInstance()回調(diào)方法中將ViewModelStore實(shí)例緩存到NonConfigurationInstances中歇万。
- 恢復(fù):在onCreate中通過getLastNonConfigurationInstance()獲取重建前的狀態(tài)并回復(fù)ViewModelStore揩晴。
- Fragment
- 緩存:在FragmentActivity.onSaveInstanceState -> FragmentManager.saveAllState -> FragmentManager.saveNonConfig方法中,將ViewModelStore實(shí)例緩存到了FragmentManagerNonConfig中贪磺,最終通過FragmentActivity將其緩存到NonConfigurationInstances中硫兰。
- 恢復(fù):方法調(diào)用棧FragmentActivity.onCreate -> FragmentManager.restoreAllState(arg1, nonConfig) -> FragmentState.instantiate(x,x,x,nonConfig, viewModelStore);其中在FragmentState.instantiate(x,x,x,nonConfig, viewModelStore)方法會創(chuàng)建一個(gè)新的Fragment并將ViewModelStore變量賦值寒锚。
以上結(jié)論是我分別從lifecycle庫1.x和2.x版本源碼分析后得出劫映。關(guān)于ViewModel的生命周期實(shí)現(xiàn)原理,各個(gè)版本實(shí)現(xiàn)略有不同刹前,其中l(wèi)ifecycle2.x版本已改用Androidx實(shí)現(xiàn)泳赋,所以ViewModel的緩存實(shí)現(xiàn)還和androidx組件版本有關(guān)系。感興趣的朋友喇喉,請自行查閱源碼祖今。
當(dāng)Fragment被detach后再attach回來,會導(dǎo)致添加多個(gè)Observer拣技?
-
分析出現(xiàn)的原因
由于Fragment默認(rèn)實(shí)現(xiàn)是在onDestroy才通知liveData 移除observers千诬,而我們每次在onCreateView都會add新的observer實(shí)例,這樣就會導(dǎo)致數(shù)據(jù)更新時(shí)膏斤,LiveData會同時(shí)通知多個(gè)Observer徐绑,界面就會快速刷新多次。
-
解決方案
- 當(dāng)你在
onActivityCreated
方法中添加LiveData.observer(LifecycleOwner owner, Observer<T> observer)
時(shí)莫辨,第一個(gè)參數(shù)使用Fragment.getViewLifecycleOwner()
方法返回值傲茄。(如果你沒有找到該方法毅访,請更新你依賴的support包版本,Google已在新版本中提供該方法) - 將
LiveData.observer(LifecycleOwner owner, Observer<T> observer)
放在onCreate回調(diào)中烫幕,在Fragment顯示時(shí)手動(dòng)觸發(fā)數(shù)據(jù)刷新俺抽,當(dāng)然最好還是更新support版本來解決。
- 當(dāng)你在
總結(jié)
Android Jetpack是一套組件開發(fā)工具集较曼,旨在幫助我們輕松構(gòu)建更穩(wěn)定、更健壯振愿、以及更可維護(hù)的應(yīng)用程序捷犹。對于Google而言,推出Jetpack可以更好的管理和維護(hù)組件庫冕末;對開發(fā)者而言萍歉,使用Jetpack可以快速開發(fā)出高質(zhì)量應(yīng)用,也能看到官方在不同技術(shù)方案上的選擇档桃,以及新技術(shù)發(fā)展方向枪孩。從目前Jetpack組件布局來看,AndroidX藻肄、kotlin是需要我們使用和掌握的蔑舞。
本文后半部分介紹了架構(gòu)組件中l(wèi)ifecycle庫的一些原理和最佳實(shí)踐,但還不夠全面深入嘹屯,后面我會一一從源碼角度分析各種組件實(shí)現(xiàn)原理攻询,敬請關(guān)注。
既然是最佳實(shí)踐州弟,怎么能沒有代碼钧栖,這里分享下作者使用架構(gòu)組件實(shí)現(xiàn)的項(xiàng)目代碼,重點(diǎn)關(guān)注wanandroid
模塊婆翔,其實(shí)現(xiàn)使用到了ViewModel+LiveData+Lifecycle+Room拯杠,按照官方推薦的架構(gòu)實(shí)現(xiàn),并完成各個(gè)組件獨(dú)立的單元測試啃奴。
限于作者水平有限潭陪,文中定有錯(cuò)誤和疏漏之處,懇請指出纺腊,與君共勉畔咧;若有不明白之處,歡迎隨時(shí)評論交流揖膜。