??ViewModel類是用來存儲(chǔ)和管理與UI相關(guān)的數(shù)據(jù)涎显,在設(shè)計(jì)之初就考慮到生命周期的影響徽惋。ViewModel允許數(shù)據(jù)在屏幕旋轉(zhuǎn)等配置變化后存活回怜。
Note: 如何在Android項(xiàng)目中引入
ViewModel
, 請(qǐng)參閱 adding components to your project.
??Android framework管理UI控制器(如Activity和Fragment)的生命周期距辆。 framework可能會(huì)決定銷毀或重新創(chuàng)建UI控制器翘紊,以響應(yīng)完全不受控制的某些用戶操作或設(shè)備事件蔽氨。
??如果系統(tǒng)銷毀或重新創(chuàng)建UI控制器,則存儲(chǔ)在其中的所有臨時(shí)的UI相關(guān)數(shù)據(jù)都將丟失帆疟。 舉個(gè)例子孵滞,您的應(yīng)用中的一個(gè)Activity可能包含用戶列表。 當(dāng)因配置更改重新創(chuàng)建Activity時(shí)鸯匹,新Activity必須重新獲取用戶列表。 對(duì)于簡(jiǎn)單數(shù)據(jù)泄伪,活動(dòng)可以使用onSaveInstanceState()方法并從onCreate()中的數(shù)據(jù)包中恢復(fù)其數(shù)據(jù)殴蓬,但是此方法僅適用于可以序列化然后反序列化的少量數(shù)據(jù),而不適用于潛在的大量數(shù)據(jù),例如用戶列表或位圖。
??另一個(gè)問題是UI控制器經(jīng)常需要異步請(qǐng)求染厅,需要一些時(shí)間才能獲取結(jié)果痘绎。 UI控制器需要管理這些請(qǐng)求,確保在系統(tǒng)銷毀自己時(shí)肖粮,清理這些請(qǐng)求以避免潛在的內(nèi)存泄漏孤页。 這種管理需要大量的維護(hù)代碼,并且在因配置更改而重新創(chuàng)建UI控制器的時(shí)涩馆,可能不得不重新發(fā)出已經(jīng)發(fā)出過的請(qǐng)求行施,這樣會(huì)浪費(fèi)許多資源。
??UI控制器(如Activity和Fragment)主要用于顯示UI數(shù)據(jù)魂那,對(duì)用戶操作做出響應(yīng)蛾号,或處理與操作系統(tǒng)之間的通信(如權(quán)限請(qǐng)求)。 再讓UI控制器負(fù)責(zé)從數(shù)據(jù)庫(kù)或網(wǎng)絡(luò)加載數(shù)據(jù)涯雅,會(huì)導(dǎo)致該類過度臃腫鲜结。 給UI控制器分配過多的工作,可能會(huì)導(dǎo)致一個(gè)類去單獨(dú)處理應(yīng)用程序的所有工作活逆,而不是將工作委托給其他類精刷。 給UI控制器分配過多的工作也使得測(cè)試工作變得更加困難。
??將視圖數(shù)據(jù)的所有權(quán)從UI控制器邏輯中分離出來蔗候,會(huì)讓項(xiàng)目更簡(jiǎn)單怒允,更高效。
實(shí)現(xiàn)ViewModel
??Architecture Components為UI控制器提供了ViewModel助手類琴庵,以便給UI準(zhǔn)備數(shù)據(jù)误算。ViewModel對(duì)象在配置更改期間會(huì)自動(dòng)保留,以便它們保存的數(shù)據(jù)可以立即提供給新的Activity或Fragment實(shí)例迷殿。 例如儿礼,您如果需要在應(yīng)用程序中顯示用戶列表,請(qǐng)確保將獲取用戶列表的工作讓ViewModel去做庆寺,而不是Activity或Fragment蚊夫,如以下示例代碼所示:
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<Users>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asyncronous operation to fetch users.
}
}
然后,您可以在Activity中按如下方式訪問用戶列表:
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
}
}
??如果重新創(chuàng)建Activity懦尝,它將接收由第一個(gè)Activity創(chuàng)建的那個(gè)MyViewModel實(shí)例知纷。 當(dāng)持有ViewModel的Activity被FINISHED之后,framework將調(diào)用ViewModel對(duì)象的onCleared()方法陵霉,以便清理資源琅轧。
Note:ViewModel絕不能持有任何View,生命周期或?qū)ontext有引用的類踊挠。
??ViewModel的壽命和View乍桂、LifecycleOwners實(shí)例無關(guān)。 這樣做也方便了您編寫覆蓋ViewModel的測(cè)試,因?yàn)樗挥萌タ紤]生命周期睹酌。 ViewModel對(duì)象可以包含LifecycleObservers权谁,例如LiveData
對(duì)象。 但是憋沿,ViewModel對(duì)象絕不能去觀察有生命周期感知能力的Observer(如LiveData對(duì)象)旺芽。 如果ViewModel需要Context,例如獲得一個(gè)系統(tǒng)服務(wù)辐啄,那么它可以擴(kuò)展AndroidViewModel類采章,并在構(gòu)造函數(shù)中有一個(gè)接收Application的構(gòu)造函數(shù),而Application是Context的子類则披。
ViewModel的生命周期
??ViewModel生命長(zhǎng)度是在獲取ViewModel時(shí)傳遞給ViewModelProvider的對(duì)象的生命周期決定的共缕。 ViewModel保留在內(nèi)存中,直到說依賴的有生命周期的對(duì)象永久消失:在Activity的情況下士复,當(dāng)它被FINISHED图谷,而在Fragment的情況下,當(dāng)它被DETACHED阱洪。
??圖1展示了一個(gè)活動(dòng)在經(jīng)歷一個(gè)循環(huán)后的各種生命周期狀態(tài)便贵,然后結(jié)束。 該圖還顯示了相關(guān)活動(dòng)生命周期旁邊的ViewModel的生命周期冗荸。 這個(gè)特定的圖表說明了一個(gè)活動(dòng)的狀態(tài)承璃。 相同的基本狀態(tài)適用于片段的生命周期。
??您通常在系統(tǒng)第一次調(diào)用Activity對(duì)象的onCreate()方法時(shí)初始化ViewModel蚌本。 系統(tǒng)可能會(huì)在Activity的整個(gè)生命周期內(nèi)多次調(diào)用onCreate()盔粹,例如當(dāng)設(shè)備屏幕旋轉(zhuǎn)時(shí)。 ViewModel的生命周期從第一次請(qǐng)求ViewModel開始程癌,直到Activity被FINISHED并銷毀舷嗡。
在Fragment之間共享數(shù)據(jù)
??Activity中Fragment們需要相互通信是很常見的事情。 想象一下嵌莉,常見的Master-Detail模式下进萄,用戶在Master Fragment的列表中選擇一個(gè)項(xiàng)目,另一個(gè)Fragment顯示所選項(xiàng)目的內(nèi)容锐峭。 這實(shí)現(xiàn)起來并不太容易中鼠,因?yàn)檫@兩個(gè)Fragment都需要定義一些接口,而它們的Activity必須將兩者聯(lián)系在一起沿癞。 此外援雇,F(xiàn)ragment必須處理另一個(gè)Fragment尚未創(chuàng)建或可見的情況。
??ViewModel可以解決這個(gè)常見的痛點(diǎn)椎扬。 Fragment可以通過一個(gè)在Activity范圍內(nèi)共享的ViewModel來處理彼此的通信熊杨,如以下示例代碼所示:
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
??請(qǐng)注意曙旭,在獲取ViewModel時(shí),兩個(gè)Fragment都要使用getActivity()晶府。這樣,這兩個(gè)Fragment會(huì)收到同一個(gè)SharedViewModel實(shí)例钻趋,該實(shí)例的作用域?yàn)锳ctivity川陆。
這樣做有以下好處:
- 這個(gè)Activity不需要做任何事情,也不需要知道Fragment之間的交流蛮位。
- 除了SharedViewModel的接口之外较沪,F(xiàn)ragment不需要了解彼此。 如果其中一個(gè)Fragment消失失仁,另一個(gè)Fragment繼續(xù)照常工作尸曼。
- 每個(gè)Fragment都有自己的生命周期,不受其他生命周期的影響萄焦。 一個(gè)Fragment替換成另一個(gè)Fragment控轿,UI繼續(xù)工作也沒有任何問題。
用ViewModel替換Loader
??像CursorLoader這樣的Loader類經(jīng)常被用來保持應(yīng)用程序中UI數(shù)據(jù)與數(shù)據(jù)庫(kù)同步拂封。 您可以使用ViewModel和其他幾個(gè)類來替換Loader茬射。 使用ViewModel將UI控制器與數(shù)據(jù)加載操作分開,這意味著您在類之間的強(qiáng)引用減少了冒签。
??Loader常見的用處是觀察數(shù)據(jù)庫(kù)的內(nèi)容在抛。 當(dāng)數(shù)據(jù)庫(kù)中的值發(fā)生更改時(shí),Loader會(huì)自動(dòng)觸發(fā)重新加載數(shù)據(jù)并更新UI:
??配合使用ViewModel萧恕、Room和LiveData來替換Loader刚梭。 ViewModel確保數(shù)據(jù)在設(shè)備配置更改后仍然存在。 當(dāng)數(shù)據(jù)庫(kù)發(fā)生變化時(shí)票唆,Room會(huì)通知你的LiveData朴读,而LiveData則用修改后的數(shù)據(jù)更新你的UI。
??這篇博客介紹了如何使用帶有LiveData的ViewModel來替換AsyncTaskLoader惰说。
??隨著你的數(shù)據(jù)變得越來越復(fù)雜磨德,你可能會(huì)選擇一個(gè)單獨(dú)的類來加載數(shù)據(jù)。 ViewModel的目的是封裝UI控制器的數(shù)據(jù)吆视,以使數(shù)據(jù)在配置更改后不受影響典挑。 有關(guān)如何在配置更改時(shí)加載,保持和管理數(shù)據(jù)的更多信息啦吧,請(qǐng)參閱Saving UI State您觉。
??Guide to Android App Architecture 建議構(gòu)建一個(gè)存儲(chǔ)庫(kù)類來處理這些功能。