因為不太喜歡mvp架構的大量備援代碼,是以呢,很長時間用着被大家诟病的mvc結構。去年呢,kotlin大量的流行了,趁着換kotlin的機會,也換上了mvvm.
一、mvvm的簡單介紹
MVVM是增強關注點分離的體系結構模式之一,它允許将使用者界面邏輯從業務(或者後端)邏輯中分離開來,他的目标(和MVC等其他目标)是為了實作”保持UI代碼簡單化,不涉及更多的業務邏輯,以便于開發者更好的控制和管理”。
MVVM主要有以下幾個層次:
1、Model層
Model層表示使用者程式的資料和業務邏輯,這一層的的推薦的實作政策之一就是觀測資料的變化并傳遞出去(供誰使用),使其從ViewModel或者其他觀察者/消費者中完全解耦
2、ViewModel層
ViewModel是和Model(資料層)進行互動,并且ViewMode可以被View觀察.ViewModel可以選擇性地為視圖提供鈎子以将事件傳遞給模型.該層的一個重要實作政策是将Model與View分離,即ViewModel不應該意識到與誰互動的視圖.
3、View層
此模式中的視圖角色是觀察(或訂閱)ViewMode,觀察資料b變化,以便于擷取資料去更新UI元素.
二、viewmodel
1、LiveData
如上所述,LiveData是新引入的元件架構之一,LiveData是一個可以被觀察的資料持有者.這也就意味着應用中的元件能夠觀察LiveData對象的更改,而無需在它們之間建立明确的和嚴格的依賴關系。這将完全分離LiveData對象使用者和LiveData對象生産者。
除此之外,LiveData還有一個很大的好處,LiveData遵守應用程式元件(活動,片段,服務)的生命周期狀态,并進元件的生命周期管理,確定LiveData對象的記憶體洩漏.
2、ViewModel
ViewModel也是新引入的體系架構元件之一.架構元件提供了一個名為ViewModel的新類,它負責為UI / View準備資料.
ViewModel為您的MVVM模式中的ViewModel提供了一個很好的基類,因為ViewModel(及其子類AndroidViewModel)的擴充類會在配置更改期間自動保留其資料.這意味着,在配
置更改後,此ViewModel所有資料可立即用于下一個活動(activity)或片段(fragment)執行個體.
viewmodel生命周期随view元件的生命周期而銷毀,不會記憶體洩露。ViewModel類是被設計用來以可感覺生命周期的方式存儲和管理 UI 相關資料,ViewModel中資料會一直存活即使 activity configuration發生變化,比如橫豎屏切換的時候。
資料庫結構往往是不能直接跟界面控件一一對應上的,是以,需要再定義一個資料對象專門對應view上的控件。而ViewModel的職責就是把model對象封裝成可以顯示和接受輸入的界面資料對象。
解耦view和model之間,viewmodel作為中間協調,分離出資料操作的職責給 ViewModel,網絡請求和資料庫操作都放到viewmodel進行
簡單的說,ViewModel就是View與Model的連接配接器,View與Model通過ViewModel實作雙向綁定。資料庫結構往往是不能直接跟界面控件一一對應上的,是以,需要再定義一個資料對象專門對應view上的控件。而ViewModel的職責就是把model對象封裝成可以顯示和接受輸入的界面資料對象。
3、viewmodel作用;
1)資料持久化
ViewModel 生命周期是貫穿整個 activity 生命周期,包括 Activity 因旋轉造成的重建立,直到 Activity 真正意義上銷毀後才會結束。既然如此,用來存放資料再好不過了。
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 async operation to fetch users
}
}
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
}
}
2)異步回調問題
通常我們 app 需要頻繁異步請求資料,比如調接口請求伺服器資料。當然這些請求的回調都是相當耗時的,之前我們在 Activity 或 fragment裡接收這些回調。是以不得不考慮潛在的記憶體洩漏情況,比如 Activity 被銷毀後接口請求才傳回。處理這些問題,會給我們增添好多複雜的工作。但現在我們利用 ViewModel 處理資料回調,可以完美的解決此痛點。
3、分擔 UI controller負擔
從最早的 MVC 到目前流行的 MVP、MVVM,目的無非是 明确職責,分離 UI controller 負擔。
UI controller 比如 Activity 、Fragment 是設計用來渲染展示資料、響應使用者行為、處理系統的某些互動。如果再要求他去負責加載網絡或資料庫資料,會讓其顯得臃腫和難以管理。是以為了簡潔、清爽、絲滑,我們可以分離出資料操作的職責給 ViewModel。
4、Fragments 間共享資料
比如在一個 Activity 裡有多個fragment,這fragment 之間需要做某些互動。我之前的做法是接口回調,需要統一在 Activity 裡管理,并且不可避免的 fragment 之間還得互相持有對方的引用。仔細想想就知道這是很翔的一件事,耦合度高不說,還需要大量的容錯判斷(比如對方的 fragment 是否還活着)。那麼用 ViewModel 是怎麼樣的呢(官網例子):(activity 與其内部的 fragment 可以共用一個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.
});
}
}
仔細體會下這樣的好處會發現:
1、Activity 不需要做任何事,甚至不知道這次互動,完美解耦。
2、Fragment 隻需要 與ViewModel互動,不需要知道對方 Fragment 的狀态甚至是否存在,更不需要持有其引用。所有當對方 Fragment 銷毀時,不影響本身任何工作。
3、Fragment 生命周期互不影響,甚至 fragment 替換成其他的 也不影響這個系統的運作。
四、注意
官網用一個大大的紅色感歎号表明:
Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the
activity context.由于 ViewModel 生命周期可能長與 activity 生命周期,是以為了避免記憶體洩漏 Google 禁止在 ViewModel 中持有 Context 或 activity 或 view 的引用。
可以發現 AndroidViewModel 類,内部維護了一個 ApplicationContext ,實在需要可以用這個
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
/**
* Return the application.
*/
@NonNull
public <T extends Application> T getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}