JetPack之ViewModel最新源碼詳細分析
本文會基于最新版ViewModel使用方法與源碼進行詳細分析,從注冊到實現ViewModel界面數據如何保存與管理全部涉及。
**
簡介:
**ViewModel
是JetPack系列庫之一,它用來對組件的界面數據進行管理,且當組件的狀態發生改變時數據依然留存。
優點:1.當所依賴組件的狀態發生改變時,例如屏幕旋轉等,界面數據不會發生改變
2.實現MVVM架構的基礎,在日常開發中,ViewModel
就是MVVM架構的VM層,它相當于MVP架構的present,我們可以將業務邏輯以及數據交互的部分放入ViewModel中。
源碼分析:
版本:viewmodel:1.1.1
此處使用的示例代碼與livedata源碼分析的相同,不影響流程分析
首先構造一個繼承于ViewModel
的類
接著創建一個factory
實例傳入ViewModelProvider(this,factory )
中去并調用get
方法將我們的ViewModel
作為參數傳入。
此處需要注意的是在以前的版本中直接通過 ViewModelProviders.of(this).get()
方法來完成這個操作的。在最新的版本中已經取消了of方法,并且構造方法必須要傳入2個參數
查看ViewModelProvider
的構造方法可看出構造ViewModelProvider
時必須傳入2個參數才行,其中第一個參數為ViewModelStore
,第二個參數為factory,
ViewModelStore
用來存儲和管理ViewModel
,factory
則用來對ViewModel
進行實例化。此時構建ViewModelProvider
時傳入的參數為所依賴的組件的ViewModelStore
,也就是說,我們通過組件的ViewModelStore
來對ViewModel
進行存儲和管理。
其中factory
需要我們在構造的時候進行傳入,ViewModelProvider
給我們提供了3種實例化的方法,分別是簡單工廠模式構造,通過反射構造實例,以及我們自己實現factory構造實例。
當我們使用ViewModelProvider.NewInstanceFactory()
構造實例時,查看源碼可看出,它實現了Factory
接口,并且在其內部的create
方法中會調用我們在之前get
方法中傳入的viewmodel
的newInstance
方法。這個方法需我們自己在viewmodel
中去實現,用來返回viewmodel
實例,如果實例化出錯則會拋出異常
public static class NewInstanceFactory implements Factory {
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
//返回modelClass實例對象
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);
}
}
}
使用AndroidViewModelFactory
方式反射生成ViewModel
,該Factory
使用單例模式進行構建 ,所以它的生命周期等同于應用程序的生命周期,且只有一個實例,推薦使用此模式來獲取factory
,當它的create
方法被調用時,會通過application利用反射創建一個ViewModel
實例并返回。
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
public AndroidViewModelFactory(@NonNull Application application) {
//構造時進行傳入
mApplication = application;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
//通過反射創建viewmodel
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
return super.create(modelClass);
}
}
第三種方式就是直接實現Factory
接口,并自己實現create
方法,我們需要在該create
方法內,返回viewmodel
的實例
/**
* Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
*/
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
* <p>
*
* @param modelClass a {@code Class} whose instance is requested
* @param <T> The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
當我們在構造ViewModelProvider
時,會將我們傳入的這2個參數進行保存,
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
當我們調用get
方法時
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
//獲取到ViewModel的類名
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
//再次進行回調
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
在該方法內首先會從ViewModelStore
中通過key去獲取ViewModel
,這個key
就是上面的DEFAULT_KEY
+類名。如果找到則直接返回,否則調用factory
的create
方法創建ViewModel
實例放入ViewModelStore
進行管理和保存并返回該實例
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//從ViewModelStore中獲取viewModel
ViewModel viewModel = mViewModelStore.get(key);
//如果找到則直接返回
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
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);
//noinspection unchecked
return (T) viewModel;
}
ViewModelStore
內部通過hashmap
來對viewmodel
進行管理,
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();
}
}
到此處為止源碼部分分析完畢,如何觸發viewmodel
對組件界面的狀態保存繼續分析
上面說過在構建ViewModelProvider
時傳入的ViewModelStore
來自于構建它所傳入的this,也就是當前組件,我們知道ViewModelStore
起到了對viewmodel
的管理作用,viewmodel
的獲取和存儲都是通過它來完成。
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
在這里調用了組件的owner.getViewModelStore()方法
this(owner.getViewModelStore(), factory);
}
追蹤getViewModelStore
方法,發現它是一個ViewModelStoreOwner
接口中的一個方法
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
這里發現它的實現類有很多個,我們一般在ViewModelProvider
中傳入的是fragment
或者activity
,而fragment其實最終也是調用的activity的getViewModelStore
方法這里查看activity
如何處理。
發現在activity的父類ComponentActivity
中有這個方法的實現,通過getViewModelStore
去獲取一個ViewModelStore
,當ViewModelStore
獲取為空時,會調用getLastNonConfigurationInstance
方法去恢復狀態改變前的NonConfigurationInstances
,和這個方法對應的是onRetainNonConfigurationInstance
,該方法會在組件狀態改變時去保存數據信息,由系統調用。
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.");
}
//如果當前組件的mViewModelStore 為空,則從NonConfigurationInstances 中去獲取
if (mViewModelStore == null) {
//返回之前
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
//如果在NonConfigurationInstances 中沒有找到,則創建一個新的ViewModelStore并保存起來
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
在代碼中ViewModelStore
會從NonConfigurationInstances
中去嘗試獲取,我們查看viewModelStore
的賦值時機即可
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
查看發現它的賦值時機就是在onRetainNonConfigurationInstance
中完成的,也就是說在組件的狀態發生改變時,會去嘗試獲取viewModelStore
,并將viewModelStore
保存在NonConfigurationInstances
中并返回。
也就是在onRetainNonConfigurationInstance
中會對viewModelStore
進行保存,在getLastNonConfigurationInstance
中會對viewModelStore
進行恢復
具體流程:
當我們在activity
中去實例化ViewModelProvider
時,所依賴的activity在其內部會調用getViewModelStore
方法去獲取一個ViewModelStore
,如果沒有則創建一個,并保存起來,然后調用ViewModelProvider.get
方法將我們的viewmodel
實例通過ViewModelStore
進行保存管理,當我們的activity的狀態發生改變,如旋轉屏幕,此時系統會調用onRetainNonConfigurationInstance
方法,在這個方法內會將我們的ViewModelStore
進行保存。
我們一般會搭配livedata
來使用viewmodel
,livedata
常用來存儲與界面相關的數據元素,當我們的activity狀態改變后,界面再次從livedata
中獲取界面數據時,由于livedata
實例由viewmodel
保管,而viewmodel
存儲在ViewModelStore
中,當前activity去獲取ViewModelStore
時會通過getLastNonConfigurationInstance
方法恢復之前的ViewModelStore
,所以狀態改變前后的ViewModelStore
是同一個,所以數據沒有發生任何改變,從而界面相關數據不會發生丟失。
智能推薦
LiveData詳細分析
目錄介紹 01.LiveData是什么東西 02.使用LiveData的優勢 03.使用LiveData的步驟 04.簡單使用LiveData 05.observe()和observerForever() 06.LiveData原理介紹 07.observe訂閱源碼分析 08.setValue發送源碼分析 09.observeForever源碼 10.LiveData源碼總結 00.使用LiveD...
Lifecycle詳細分析
Lifecycle源碼分析 目錄介紹 01.Lifecycle的作用是什么 02.Lifecycle的簡單使用 03.Lifecycle的使用場景 04.如何實現生命周期感知 05.注解方法如何被調用 06.addObserver調用分析 07.知識點梳理和總結一下 00.使用AAC實現bus事件總線 利用LiveData實現事件總線,替代EventBus。充分利用了生命周期感知功能,可以在act...
ConcurrentHashmap 詳細分析
詳盡的分析 JDK8 后的ConcurrentHashmap,思路分析輔以源碼走讀,徹底讀懂 ConcurrentHashmap。 簡介 放入數據 容器元素總數更新 容器擴容 協助擴容 遍歷 簡介 在從 JDK8 開始,為了提高并發度,ConcurrentHashMap的源碼進行了很大的調整。在 JDK7 中,采用的是分段鎖的思路。簡單的說,就是ConcurrentHashMap是由多個HashM...
ION詳細分析
參考: http://blog.csdn.net/armwind/article/details/53454251?locationNum=2&fps=1 代碼路徑 驅動代碼: kernel-3.18/drivers/staging/android/ion Native lib代碼: system\core\libion & vendor/mediatek/proprietary/...
CMA 詳細分析
關于CMA的config @LINUX/android/kernel/arch/arm/configs/msm8909_defconfig CONFIG_CMA=y 已經打開 # CONFIG_CMA_DEBUG is not set # # Default contiguous memory area size: # CONFIG_CMA_SIZE_MBYTES=8 //兩個配對定義 CONFI...
猜你喜歡
MapReduce詳細分析
一、MapReduce概述 1、定義 MapReduce核心功能是將用戶編寫的業務邏輯代碼和自帶默認組件整合成一個完整的分布式運算程序,并發運行在一個Hadoop集群 上。 2、MR進程 一個完整的MapR educe程序在分布式運行時有三類實例進程: **Mr AppMaster:**負責整個程序的過程調度及狀態協調。 MapTask:負責Map階段的整個數據處理流程。 ReduceTask:負...
freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...
requests實現全自動PPT模板
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...