費了不少腦細胞,把Lifecycle單拆出來整了一篇文章。那麼接下來天然而然的就到了ViewModel,爲了讓系列像系列的樣子,因此這裏仍然是單獨把ViewModel拿出來。java
你別說單獨抽出來,還真有點乾乾巴巴,麻麻賴賴,一點都不圓潤。那還說啥呢?盤它...android
一點點入坑JetPack:實戰前戲NetworkBoundResource篇app
新官上任三把火,強敵面前秀三波。對於ViewModel來講,它算是JetPack框架中堪當中樞的角色,說實話它實在很差單獨去聊,更多的是和LiveData共進退。這裏必須安利一下,ViewModel+LiveData的確很好用,甚至可能加上Room簡直...飄了,拽了,感受本身個頭都不矮了;瘋了,狂了,敢在宇宙之間稱王了....異步
礙於篇幅的緣由,這裏單獨聊ViewModel,後邊會綜合介紹展示其強大的戰鬥力...ide
關於ViewModel來講,其實仍是蠻簡單的。從ViewModel官方的描述來看ViewModel的存在,解決了倆大問題:post
咱們都知道,當咱們的Activity/Fragment由於某些因素被銷燬重建時,咱們的成員變量便失去了意義。所以咱們經常須要經過 onSaveInstanceState()和onCreate()/onSaveInstanceState(Bundle)完成對數據的恢復(一般還要保證其正確的序列化)。而且對於大型數據來書,便有些乏力,好比:List、Bitmap...
而ViewModel就是解決此問題。
另外一個問題是Activity/Fragment常常須要進行一些異步操做。一旦涉及到異步,咱們都明白這裏存在內存泄漏的可能性。所以咱們保證Activity/Fragment在銷燬後及時清理異步操做,以免潛在的內存泄漏。
ViewModel並無自動幫咱們解決這個問題,而是經過onCleared()
交給咱們業務本身重寫去處理。
關於ViewModel的使用,實在沒啥好說的。實在是太簡單了,一個簡單的demo:
class MyViewModel : ViewModel() {
var name: String = "MDove"
}
// Activity中調用
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val model = ViewModelProviders.of(this).get(MyViewModel::class.java)
// TODO model.name
}
}
複製代碼
咱們只須要將想要被保存、被管理的變量,聲明在ViewModel的實現類中便可。而後經過ViewModelProviders.of()/get()
拿到這個實例。就可能像往常同樣自由的使用,而不須要擔憂Activity/Fragment重建所帶來的一系列問題。
文檔在此處,有一個大大的警告:Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.
爲啥?從上述解決的問題來看,ViewModel很明顯生命週期會比Activity要長,所以若是持有Activity相關實例,必然會帶來內存泄漏。(那若是的確有業務須要咋整?使用AndroidViewModel(application)
便可。)
值得注意的一點:of方法須要傳遞一個Activity/Fragment。由於ViewModel須要與其生命週期綁定。既然能夠傳遞一個Activity,那麼咱們就可以猜到:是否是對於此Activity下的Fragment這個ViewModel也是可見的?
沒錯,正是如此。官方也做出瞭解讀:Activity中的兩個或多個Framgent須要相互通訊是很常見的,這個常見的痛點能夠經過使用ViewModel對象來解決,這些Fragment能夠共享ViewModel來處理通訊問題。
因此咱們在同Activity下,不一樣的Fragment實例,能夠直接經過傳入activity,拿到一樣的ViewModel實例,進而實現數據通信。
真的很方便...
若是咱們打開ViewModel的源碼,咱們會發現...
public abstract class ViewModel {
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}
複製代碼
就是一個抽象類,沒錯,整個ViewModel的設計就是很簡潔,咱們往ViewModelProviders中繼續看:
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
複製代碼
咱們能夠看到在實例化ViewModelProvider中,須要傳一個ViewModelStore,而這個ViewModelStore直接經過傳入的FragmentActivity中拿,讓咱們走進去看一看:
@NonNull
@Override
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;
}
複製代碼
咱們能夠看到,這個ViewModelStore是在FragmentActivity中是一個mViewModelStore的變量。這個ViewModelStore是什麼?從名字能夠看出它是一個ViewModel的Store。
ViewModelStore的很簡單,就是一個Map在後文中會展開。
最開始我看到這時,很懵。ViewModel是保證咱們重建後實例的惟一,但是這居然是一個成員變量,很明顯重建後變量就沒了?!...(PS:固然有這種疑問,是由於我本身蠢...)
怎麼肥死,小老弟??...其實這裏是沒問題的,咱們仔細看一看,這個mViewModelStore賦值是經過這一行代碼:
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
複製代碼
沒錯,就是這行代碼,保證了咱們重建後恢復原來的mViewModelStore,進而保證了咱們的ViewModel的惟一性。
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
複製代碼
mViewModelStore源碼 -> ViewModelStore源碼,很常見的Map存儲操做
//
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
複製代碼
從ViewModel的使用上來講,彷佛並無什麼「船新的版本」...更多的是幫咱們搞定了一些現存的坑。的確是如此,但其實ViewModel更可能是帶來了一種思想:數據驅動,也就是MVVM。
ViewModel做爲中樞,擔任了從數據源拿數據,交由LiveData通知UI層更新UI。用一張圖來解釋這種變革:
Google Sample爲Repository的編寫,提供了一個很巧妙的設計:NetworkBoundResource
。全類一共有120+的代碼,卻基於LiveData+ViewModel幫咱們約束了:從服務器取、從數據庫取、網絡獲取失敗,從數據庫取...等等一系列網絡請求、本地請求約數。
關於這個類的設計與用法,會在後續的實戰篇一點點展開。沒錯,當你用上它們,你會愛上這款「遊戲」。
今天的文章想聊的內容就到此結束了,更多的是ViewModel的一個引子。畢竟對於咱們來講,我tm不須要知道這些,只須要告訴我怎麼寫就行。老夫寫代碼就是ctrl+c/v!
不着急,一點點來。後邊我會把業務中正在運行的代碼拿出來,作實戰操做分析。飯要一口口的吃,文章要一篇篇的寫...