網站首頁 編程語言 正文
前言
學習ViewModel之前首先我們得簡單了解下MVP和MVVM,因為ViewModel是MVVM中的一個元素
MVP
MVVM
在MVP中View想要調用Model數據層,需要經過中間層Presenter, 這樣就實現了View和Model的解耦,這也是MVP和MVC的差別; 但是如果一個Activity中有太多交互,那么我們的View接口數量就會很龐大達到十幾個也不足為奇,并且在View層調用了Presenter之后,會反過來調用View層,這樣顯得繁瑣;而MVVM的出現就解決了這個問題
說到MVVM的話,我們放上Google的架構圖
MVVM中的VM指的就是ViewModel; 從上圖為沒看到,因為ViewModel中持有了LiveData,而LiveData是一個可觀察的數據類型,在LiveData原理篇中,我們做了詳細的分析;在View層中,將被觀察的數據LiveData訂閱,并提供了一個觀察者Observer,當數據發生變化的時候,就會回調Observer中的onChanged()方法,從而更新UI, 這個過程是系統源碼幫我們處理的,所以就沒有上面Presenter中調用View的那一步了
ViewModel概述
應用的某個 Activity 中可能包含用戶列表,因配置更改而重新創建 Activity 后,新 Activity 必須重新提取用戶列表; 對于簡單的數據,Activity 可以使用onSaveInstanceState() 方法從 onCreate() 中的捆綁包恢復其數據,但此方法僅適合可以序列化再反序列化的少量數據,而不適合數量可能較大的數據,如用戶列表或位圖,使用ViewModel可以解決這個問題
另外,界面控制器經常需要進行異步調用,這些調用可能需要一些時間才能返回結果; 界面控制器需要管理這些調用,并確保系統在其銷毀后清理這些調用以避免潛在的內存泄露;此項管理需要大量的維護工作,并且在因配置更改而重新創建對象的情況下,會造成資源的浪費,因為對象可能需要重新發出已經發出過的調用,使用ViewModel可以解決這個問題
諸如 Activity 和 Fragment 之類的界面控制器主要用于顯示界面數據、對用戶操作做出響應或處理操作系統通信(如權限請求); 如果要求界面控制器也負責從數據庫或網絡加載數據,那么會使類越發膨脹。為界面控制器分配過多的責任可能會導致單個類嘗試自己處理應用的所有工作,而不是將工作委托給其他類;以這種方式為界面控制器分配過多的責任也會大大增加測試的難度
ViewModel 類旨在以注重生命周期的方式存儲和管理界面相關的數據。ViewModel 類讓數據可在發生屏幕旋轉等配置更改后繼續存在
ViewModel使用
ViewModel的使用比較簡單,我們想要使用使用的話直接繼承ViewModel或者繼承AndroidViewModel即可; AndroidViewModel源碼如下,他們倆的區別,是AndroidViewModel中多了一個Application的成員變量以及以Application為參數的構造方法,如果你需要Application的話,就直接繼承AndroidViewModel即可
public class AndroidViewModel extends ViewModel { @SuppressLint("StaticFieldLeak") private Application mApplication; public AndroidViewModel(@NonNull Application application) { mApplication = application; } /** * Return the application. */ @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"}) @NonNull public <T extends Application> T getApplication() { return (T) mApplication; } }
這里以我寫的一個Demo為例,這個Demo可以在Github上找到,鏈接如下JetPack Demo,這個Demo用到了所有Jetpack的組件,是學習Jetpack的輔助資料,需要的小伙伴可以下載和star。我們自定義一個AppsViewModel
如下:
class AppsViewModel(appsRepository: AppsRepository) : ViewModel() { val apps: LiveData<List<AppEntity>> = appsRepository.loadApps() }
因為我們這里傳入了一個參數,所以需要定義一個Factory,代碼如下:
class AppsViewModelFactory(private val repository: AppsRepository) : ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { return AppsViewModel(repository) as T } }
接下來就是使用了:
class AppsListFragment : Fragment() { private lateinit var viewModel: AppsViewModel //-----1----- private val viewModel: AppsViewModel by viewModels { FactoryProvider.providerAppsFactory(requireContext()) } override fun onActivityCreated(savedInstanceState: Bundle?) { //-----2----- viewModel = ViewModelProviders.of(this,FactoryProvider.providerAppsFactory(requireContext())) .get(AppsViewModel::class.java) //-----3----- viewModel.apps.observe(viewLifecycleOwner, Observer { //Update UI }) } }
我們先聲明了一個變量viewModel,我們可以通過注釋1處的 by viewModels提供一個自定義的Factory,但是需要添加一個依賴:implementation "androidx.fragment:fragment-ktx:1.2.2;或者采用注釋2的方式直接使用ViewModelProviders.of(fragment,factory).get(class)的形式獲取實例。 然后就直接使用了,在注釋3處使用viewmodel.apps.observe將其加入生命周期觀察中,如果數據發生變化就會調用Observer的回調,從而更新UI
ViewModel源碼
上面我們采用ViewModelProviders.of(...).get(class)
方法獲取ViewModel,我們就從這里開始源碼開始分析,我們先看下這個類的源碼:
@Deprecated public class ViewModelProviders { @Deprecated public ViewModelProviders() { } @Deprecated @NonNull @MainThread public static ViewModelProvider of(@NonNull Fragment fragment) { return new ViewModelProvider(fragment); } @Deprecated @NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity) { return new ViewModelProvider(activity); } @Deprecated @NonNull @MainThread public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) { if (factory == null) { factory = fragment.getDefaultViewModelProviderFactory(); } return new ViewModelProvider(fragment.getViewModelStore(), factory); } @Deprecated @NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { if (factory == null) { factory = activity.getDefaultViewModelProviderFactory(); } return new ViewModelProvider(activity.getViewModelStore(), factory); } @SuppressWarnings("WeakerAccess") @Deprecated public static class DefaultFactory extends ViewModelProvider.AndroidViewModelFactory { @Deprecated public DefaultFactory(@NonNull Application application) { super(application); } } }
我們看到此類中提供了四個方法,其實著四個都以一樣,我們以第四個為例分析;第一個參數是Activity,第二個參數是一個Factory,默認情況下我們是不需要傳入Factory這個參數的。如果不傳入的話,我們跟進構造方法就能看到默認是用NewInstanceFactory來作為Factory的,看到內部的create方法通過反射生成了一個ViewModel,實例源碼如下:
public static class NewInstanceFactory implements Factory { private static NewInstanceFactory sInstance; /** * Retrieve a singleton instance of NewInstanceFactory. * * @return A valid {@link NewInstanceFactory} */ @NonNull static NewInstanceFactory getInstance() { if (sInstance == null) { sInstance = new NewInstanceFactory(); } return sInstance; } @SuppressWarnings("ClassNewInstance") @NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { //noinspection TryWithIdenticalCatches try { return modelClass.newInstance(); } catch (InstantiationException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (IllegalAccessException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } } }
- 如果傳入Factory參數的話,就會用我們自定義的Factory作來生成ViewModel。
-
of
方法調用結束返回的是ViewModelProvider
, 然后調用的是get
方法,我們看下這個類的部分源碼:
public class ViewModelProvider { private static final String DEFAULT_KEY ="androidx.lifecycle.ViewModelProvider.DefaultKey"; private final Factory mFactory; private final ViewModelStore mViewModelStore; //-----1----- public ViewModelProvider(@NonNull ViewModelStoreOwner owner) { this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory() : NewInstanceFactory.getInstance()); } public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) { this(owner.getViewModelStore(), factory); } public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; mViewModelStore = store; } public <T extends ViewModel> T get(@NonNull Class<T> modelClass) { String canonicalName = modelClass.getCanonicalName(); if (canonicalName == null) { throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels"); } 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)) { if (mFactory instanceof OnRequeryFactory) { ((OnRequeryFactory) mFactory).onRequery(viewModel); } return (T) viewModel; } else { //noinspection StatementWithEmptyBody if (viewModel != null) { // TODO: log a warning. } } if (mFactory instanceof KeyedFactory) { viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass); } else { viewModel = (mFactory).create(modelClass); } mViewModelStore.put(key, viewModel); return (T) viewModel; } }
上面截取的部分源碼其實是ViewModelProvider最重要的方法了,我們先看此類中有兩個重要的成員變量,其中一個是mFactory, 注釋1處看到如果我們沒有傳入factory的話,默認實現的是NewInstanceFactory, 印證了我們之前的說法。還有一個是ViewModelStore, 它是怎么來的呢?因為ComponentActivity中實現了接口ViewModelStoreOwner,在ViewModelProvider的構造方法中調用owner.getViewModelStore(),這個owner就是ComponentActivity自身,然后獲取到了ViewModelStore這個變量,實際調用的源碼如下:
@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."); } //-----1----- 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; }
在注釋1處,判斷如果mViewModelStore == null的話,就會調取getLastNonConfigurationInstance嘗試獲取,如果獲取到了就將獲取到的賦值給mViewModelStore返回。這里就涉及到一個重要的知識點了,為什么說ViewModel在橫豎屏切換的時候能夠持久的保存數據,不需要像之前一樣調用onSaveInstanceState? 因為在Activity被銷毀的時候,還會調用另外一個方法onRetainNonConfigurationInstance, 我們看它在ComponentActivity中的源碼實現:
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; } //----1----- NonConfigurationInstances nci = new NonConfigurationInstances(); nci.custom = custom; nci.viewModelStore = viewModelStore; return nci; }
我們看到在注釋1的地方將我們之前存在的viewModelStore存儲到NonConfigurationInstances中了,然后在調用getViewModelStore的時候調用getLastNonConfigurationInstance這樣就保證了Activity銷毀之前和之后的viewModelStore是同一個,那它里面存儲的ViewModel值也就是同樣的了。所以ViewModel的生命周期可以用下圖來概括:
接下來我們分析get
方法:
private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey" public <T extends ViewModel> T get(@NonNull Class<T> modelClass) { String canonicalName = modelClass.getCanonicalName(); if (canonicalName == null) { throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels"); } return get(DEFAULT_KEY + ":" + canonicalName, modelClass); }
首先獲取類的全稱的字符串名字,和DEFAULT_KEY
拼湊成一個Key,然后調用get
的重載方法如下:
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) { ViewModel viewModel = mViewModelStore.get(key); //-----1----- if (modelClass.isInstance(viewModel)) { if (mFactory instanceof OnRequeryFactory) { ((OnRequeryFactory) mFactory).onRequery(viewModel); } return (T) viewModel; } else { //noinspection StatementWithEmptyBody if (viewModel != null) { // TODO: log a warning. } } if (mFactory instanceof KeyedFactory) { viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass); } else { //-----2----- viewModel = (mFactory).create(modelClass); } mViewModelStore.put(key, viewModel); return (T) viewModel; }
注釋1處判斷我們的modelClass是不是屬于ViewModel類型的,并且判斷mFactory的類型是否屬于OnRequeryFactory類型,如果是的話,就返回值; 在注釋2處使用Factory通過反射創建一個viewModel, 然后將其存入mViewModelStore中。我們看下ViewModelStore的源碼:
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); } Set<String> keys() { return new HashSet<>(mMap.keySet()); } /** * Clears internal storage and notifies ViewModels that they are no longer used. */ public final void clear() { for (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); } }
ViewModelStore
的源碼很簡單,內部持有了一個HashMap對象,用來存放ViewModel。ViewModel的創建到此就結束了。然后就是使用的問題, 使用如下
override fun onActivityCreated(savedInstanceState: Bundle?) { viewModel = ViewModelProviders.of(this,FactoryProvider.providerAppsFactory(requireContext())) .get(AppsViewModel::class.java) viewModel.apps.observe(viewLifecycleOwner, Observer { //Update UI }) }
ViewModel的使用涉及到LiveData和Lifecycle部分,這里就不再多說了
原文鏈接:https://blog.csdn.net/m0_70748845/article/details/126122605
相關推薦
- 2022-07-20 上傳文件至Github倉庫
- 2024-03-28 存儲過程整合springboot
- 2022-05-02 Python實現PDF文字識別提取并寫入CSV文件_python
- 2022-05-17 bat批處理腳本中文亂碼的解決_DOS/BAT
- 2022-11-14 Spring中存取Bean對象的相關注解
- 2022-11-09 WPF使用WrapPanel實現虛擬化效果_C#教程
- 2023-04-27 阿里低代碼框架lowcode-engine自定義設置器詳解_React
- 2022-04-01 ?python中pandas讀取csv文件?時如何省去csv.reader()操作指定列步驟_pyt
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支