在《也談Android應(yīng)用架構(gòu)》中我們對MVC、MVP岔乔、MVVM進(jìn)行了詳盡的分析,但還有一個(gè)問題懸而未決滚躯,那就是生命周期雏门。在Android平臺(tái)上生命周期具有十分重要的意義,因此這也是架構(gòu)必須考慮的因素之一掸掏。生命周期處理不好很容易發(fā)生內(nèi)存泄漏剿配,但對架構(gòu)而言,真正困擾我們的卻不是內(nèi)存泄漏的問題阅束,反而是因生命周期太短呼胚,實(shí)例被銷毀重建,從而產(chǎn)生一系列不必要的行為息裸。這種情況發(fā)生的場景主要在屏幕旋轉(zhuǎn)以及頁面被系統(tǒng)回收時(shí)蝇更。
Activity難免需要依賴網(wǎng)絡(luò)、數(shù)據(jù)庫等數(shù)據(jù)來渲染頁面呼盆,當(dāng)屏幕旋轉(zhuǎn)時(shí)年扩,Activity重建,因而數(shù)據(jù)需要重新加載访圃,但這完全沒有必要厨幻。一種策略是對數(shù)據(jù)進(jìn)行緩存,這是一種可考慮的方案腿时,但它只解決了一半的問題况脆,如果Activity重建發(fā)生在數(shù)據(jù)返回前,此時(shí)根本來不及緩存批糟,下一次請求就迅速地發(fā)生了格了。
在MVP、MVVM架構(gòu)中徽鼎,數(shù)據(jù)由M來提供盛末,但真正和生命周期打交道的是P和VM,我們得從這里著手解決生命周期的問題否淤。再明確地說一遍悄但,我們要解決的問題是不論在數(shù)據(jù)返回前還是返回后,在屏幕旋轉(zhuǎn)這種場景下都不需要多次加載數(shù)據(jù)石抡。這個(gè)問題由兩種狀態(tài)組成:加載中和加載完成后檐嚣,對于前者我們要知道當(dāng)前正在加載數(shù)據(jù),對于后者則只需要把數(shù)據(jù)緩存起來即可汁雷。
對數(shù)據(jù)緩存很簡單净嘀,但加載中的狀態(tài)就要好好斟酌一番了报咳,我們可以輕易地給這個(gè)狀態(tài)加標(biāo)記,但隨著重建這個(gè)標(biāo)記也會(huì)被回收挖藏,由此可以想到兩種應(yīng)對之法暑刃,一是讓P和VM不被回收,這樣就可以進(jìn)行標(biāo)記了膜眠,二是讓當(dāng)前這個(gè)加載不被回收岩臣,也就是其生命周期不和P與VM同步。不讓P和VM回收宵膨,有以下幾種方式:
配置
android:configChanges="orientation|keyboardHidden|screenSize"
onRetainCustomNonConfigurationInstance()
/getLastCustomNonConfigurationInstance()
- 繼承Fragment
除了配置configChanges架谎,其余兩種方式都是不錯(cuò)的解決辦法。除此之外還有一種方式可以同時(shí)實(shí)現(xiàn)我們說的兩種應(yīng)對之法辟躏,這就是Loader谷扣。關(guān)于什么是Loader以及Loader如何保持P和VM不被回收,大家可以自行查閱相關(guān)資料捎琐,如何保持一個(gè)加載任務(wù)不被回收会涎,可以參閱architecture-samples
,并切換到分支deprecated-todo-mvp-loaders
瑞凑。
我們不打算大刀闊斧地講述每個(gè)方案的細(xì)節(jié)和優(yōu)缺點(diǎn)末秃,因?yàn)殡S著Jetpack誕生,這種復(fù)雜又費(fèi)力的方案系統(tǒng)已經(jīng)幫我們完成了籽御,我們只需要了解系統(tǒng)是如何處理的即可练慕。從書寫代碼變成查看代碼,可以說大大減少了我們對生命周期的“怨恨”技掏,不得不說Google這波操作很圈粉呢铃将。在這里,我們只關(guān)注Lifecycle零截、ViewModel和LiveData三部分麸塞。
Lifecycle
生命周期讓人困擾的很大一部分原因是只有Activity這樣的系統(tǒng)組件才可以感知生命周期的變化,而Lifecycle的出現(xiàn)則把這種感知力放大到了任何類涧衙。Lifecycle的原理很簡單,當(dāng)生命周期變化時(shí)奥此,Activity通知到Lifecycle弧哎,其他類就可以通過Lifecycle感知生命周期的變化了。
Lifecycle的核心就三個(gè)類:Lifecycle稚虎、LifecycleOwner和LifecycleObserver撤嫩。從名字就可以輕易看出這是一個(gè)觀察者模式,Activity作為LifecycleOwner蠢终,把生命周期的變化反映到Lifecycle序攘,Lifecycle再通知給所有的LifecycleObserver即可茴她。這個(gè)概念太簡單了,就不在此贅述源碼了(看了一下沒有什么亮點(diǎn)~)程奠,不過如果你感興趣丈牢,請注意一下ReportFragment這個(gè)類,Activity的生命周期就是通過它來通知Lifecycle的(添加一個(gè)看不見的Fragment瞄沙,這個(gè)操作似乎似曾相識(shí)己沛?)。
Lifecycle只是讓P和VM獲得了生命周期感知能力距境,并沒有解決如何保持的問題申尼,不過它是我們后面內(nèi)容的基礎(chǔ),所以還是很有必要了解一番垫桂。
ViewModel
這個(gè)ViewModel其實(shí)就是MVVM中的VM师幕,但經(jīng)過Google的加工之后具備了很好的生命周期感知能力,這就是我們苦苦追尋的東西呀∥芴玻現(xiàn)在我們就對它抽絲剝繭霹粥,看看系統(tǒng)是如何完成這件事的。
ViewModel的使用非常簡單碱呼,就是一句話:
class LoginActivity : AppCompatActivity() {
private lateinit var loginViewModel: LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
loginViewModel = ViewModelProvider(this, LoginViewModelFactory()).get(LoginViewModel::class.java)
}
}
當(dāng)重建發(fā)生時(shí)蒙挑,LoginActivity、ViewModelProvider都是新的實(shí)例愚臀,但是LoginViewModel一定得是原來的實(shí)例忆蚀,這說明它在某處被緩存了起來。先看下ViewModelProvider做了什么吧:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
// ...
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
// ...
return (T) viewModel;
} else {
// ...
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
非常簡單姑裂,從Activity獲取到了一個(gè)ViewModelStore馋袜,如果里面包含了LoginViewModel就直接取出來,否則新建一個(gè)并緩存到ViewModelStore里舶斧。那么ViewModelStore是什么欣鳖,它是如何保持下來的?
ViewModelStore里維護(hù)了一個(gè)Map茴厉,存儲(chǔ)ViewModel實(shí)例泽台,僅此而已。AppCompatActivity實(shí)現(xiàn)了ViewModelStoreOwner接口矾缓,里面只有一個(gè)方法getViewModelStore怀酷,它的實(shí)現(xiàn)如下:
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
這里出現(xiàn)了一個(gè)getLastNonConfigurationInstance(),我們在前面提過一個(gè)getLastCustomNonConfigurationInstance()方法嗜闻,那么應(yīng)該也有一個(gè)onRetainNonConfigurationInstance()與之對應(yīng)蜕依,它的實(shí)現(xiàn)如下:
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
一切都很明了了,系統(tǒng)用的是和我們一樣的方法,只是方法名稱稍有區(qū)別而已样眠。ViewModelStore里緩存了ViewModel實(shí)例友瘤,那么在Activity真正銷毀時(shí)肯定需要清空,ViewModel和ViewModelStore都提供了一個(gè)clear()方法檐束,ViewModelStore的clear方法實(shí)現(xiàn)如下:
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
它會(huì)調(diào)用其中每個(gè)ViewModel的clear方法使我們有機(jī)會(huì)清除一些數(shù)據(jù)或任務(wù)辫秧,然后就將Map清空了。在Activity中它是這樣被調(diào)用的:
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
isChangingConfigurations()用以標(biāo)識(shí)Activity執(zhí)行onDestory方法后是否準(zhǔn)備重建厢塘,只有不重建時(shí)才會(huì)清空ViewModel茶没,所以只要在clear時(shí)清理數(shù)據(jù)和中斷任務(wù)就好了。
現(xiàn)在我們解決了ViewModel實(shí)例保持的問題晚碾,接下來讓我們再想想應(yīng)該怎么解決數(shù)據(jù)重復(fù)加載的問題抓半。數(shù)據(jù)重復(fù)加載主要是因?yàn)橐粋€(gè)異步任務(wù)被多次調(diào)用,例如請求一個(gè)列表數(shù)據(jù)時(shí)格嘁,如果屏幕發(fā)生旋轉(zhuǎn)笛求,以下方法會(huì)被多次調(diào)用:
fun getUsers(){
executor.execute {
val users = model.getUsers()
handler.post{
view?.getUsers(users)
}
}
}
按照之前的說法,可以給加載任務(wù)加上標(biāo)記糕簿,當(dāng)它正在加載中就等待它加載完成探入,如果已經(jīng)加載完就取緩存的數(shù)據(jù),但是這太復(fù)雜了懂诗,稍有不慎就會(huì)出問題蜂嗽。如何讓事情變得簡單一些,出錯(cuò)率低一些呢殃恒?
要想避免此問題植旧,最好的方式是只調(diào)用一次getUsers()方法,那這個(gè)方法就不能由Activity來調(diào)用了离唐,需要ViewModel自己調(diào)用病附,等它拿到結(jié)果后反過來通知Activity。這不就是MVVM嗎亥鬓?現(xiàn)在我們總算明白為什么被系統(tǒng)實(shí)現(xiàn)的這個(gè)類叫ViewModel了完沪,因?yàn)樗褪菫镸VVM量身定制的。
關(guān)于數(shù)據(jù)反過來通知Activity這件事嵌戈,也不需要擔(dān)心覆积,因?yàn)橄到y(tǒng)照樣幫我們實(shí)現(xiàn)了,這就是LiveData熟呛。
LiveData
可觀察的數(shù)據(jù)并不是只有LiveData技健,但LiveData有自己獨(dú)特的本領(lǐng),它也具備生命周期感知力惰拱。LiveData只有在有效的生命周期范圍內(nèi)通知觀察者,并在生命周期結(jié)束后自動(dòng)移除觀察者,僅這一點(diǎn)就足夠讓它脫穎而出偿短。我們可以從它的observe方法欣孤,了解它處理生命周期的大致流程。
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper);
}
這里創(chuàng)建了一個(gè)LifecycleBoundObserver來觀察Activity的生命周期昔逗,我們看看它做了哪些工作吧:
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
// ...
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
// ...
}
它實(shí)現(xiàn)了LifecycleEventObserver降传,并在DESTROYED狀態(tài)時(shí)移除了觀察者,其后只是調(diào)用了一個(gè)activeStateChanged方法勾怒,這個(gè)方法實(shí)現(xiàn)如下:
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
onActive();
}
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
if (mActive) {
dispatchingValue(this);
}
}
這里通過是否active來分發(fā)數(shù)據(jù)婆排,在dispatchingValue中會(huì)通知所有的觀察者。
LiveData實(shí)際上是一個(gè)雙層的觀察者模式笔链,它通過觀察Lifecycle得知是否active段只,在此充當(dāng)?shù)氖怯^察者。當(dāng)它的值發(fā)生變化或者監(jiān)聽到Lifecycle變化時(shí)再通知到它的觀察者鉴扫,在此又充當(dāng)被觀察者赞枕。如此它就具備了我們想要的一切能力坪创。
總結(jié)
現(xiàn)在我們的架構(gòu)“本地化”工作又前進(jìn)了一大步,它終于在生命周期方面也不存在問題了莱预,使用Lifecycle+ViewModel+LiveData組合,解決了架構(gòu)最棘手的問題依沮,也把MVVM推向了另一個(gè)高度涯贞。當(dāng)然這并不代表著MVP就徹底敗下陣來,畢竟生命周期問題只影響了初始化時(shí)的數(shù)據(jù)悉抵,大量場景下還是有無數(shù)的交互行為,需要根據(jù)用戶的操作主動(dòng)加載各種各樣的數(shù)據(jù)傻谁,這種情況下,MVP的直觀性要遠(yuǎn)遠(yuǎn)強(qiáng)于MVVM列粪,這個(gè)特點(diǎn)也可以簡單理解為MVP適合復(fù)雜交互場景审磁,MVVM適合展示型場景。因此我們應(yīng)該根據(jù)具體場景靈活選用MVP和MVVM岂座,甚至在某些情況下可以合二為一。
還是那句話费什,沒有最好的架構(gòu),只有最適合當(dāng)前場景的架構(gòu)。
我是飛機(jī)醬泉懦,如果您喜歡我的文章疹瘦,可以關(guān)注我~
編程之路,道阻且長言沐。唯,路漫漫其修遠(yuǎn)兮险胰,吾將上下而求索。