截止到目前爲止,JetpackNote源碼分析的文章已經有四篇文章了,這一系列的文章我的初衷是想仔細研究一下Jetpack,最終使用Jetpack組件寫一個Demo,上一篇已經分析了
LiveData
,本篇文章將分析ViewModel.
1.背景
Jetpack源碼解析系列文章:
1. Jetpack源碼解析—看完你就知道Navigation是什麼了?
2. Jetpack源碼解析—Navigation爲什麼切換Fragment會重繪?
3. Jetpack源碼解析—用Lifecycles管理生命週期
4. Jetpack源碼解析—LiveData的使用及工作原理
上篇我們對LiveData
進行了分析,已清楚了它的主要作用,我們再來溫習一下:
LiveData是一個可以感知Activity、Fragment生命週期的數據容器。其本身是基於觀察者模式設計的,當
LiveData
所持有的數據發生改變時,它會通知對應的界面所持有該數據的UI進行更新,並且LiveData
中持有Lifecycle
的引用,所以只會在LifecycleOwner
處於Active
的狀態下通知數據改變,果數據改變發生在非 active 狀態,數據會變化,但是不發送通知,等owner
回到 active 的狀態下,再發送通知.而
LiveData
配合今天所講的ViewModel
使用起來真的是非常方便,接下來我們開始分析:
2.簡介
2.1 是什麼?
簡單來說:ViewModel
是以關聯生命週期的方式來存儲和管理UI相關數據的類,即使configuration
發生改變(例如屏幕旋轉),數據仍然可以存在不會銷燬.
舉個例子來說:我們在開發中經常會遇到這種場景,當我們的Activity和Fragment被銷燬重建之後,它們中的數據將會丟失,而我們一般的解決方案就是通過onSaveInstanceState()
中保存數據,然後在onCreate()
中恢復過來,但是當這些數據較大時或者數據可能又需要重新請求接口,這時候ViewModel就派上用場了,也就是說當我們把數據保存在ViewModel中,不管Activity和Fragment被銷燬了還是屏幕旋轉導致configuration發生了變化,保存在其中的數據依然存在。
不僅如此,ViewModel還可以幫助我們輕易的實現Fragment及Activity之間的數據共享和通信,下面會詳細介紹。
2.2 ViewModel生命週期
先來看一下官方給出的圖:
圖中展示了當一個Activity經過屏幕旋轉後的生命週期狀態改變,右側則是ViewModel的生命週期狀態。我們可以看到ViewModel的生命週期並不會跟隨着Activity的生命週期變化而變化,雖然ViewModel是由該Activity創建的。我們會在源碼分析部分再去看ViewModel的生命週期具體是怎樣的。下面我們看下ViewModel該怎樣使用?
3. 基本使用
3.1 數據存儲
我們參考官方Demo實現一個計時器的功能,並且演示當屏幕發生旋轉時,計時器會不會重新啓動:
DemoViewModel
class DemoViewModel : ViewModel() {
var time: Long? = null
var seekbarValue = MutableLiveData<Int>()
}
ViewModelFragment
class ViewModelFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_view_model, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val vm = ViewModelProviders.of(this).get(DemoViewModel::class.java)
if (vm.time == null) {
vm.time = SystemClock.elapsedRealtime()
}
chronometer.base = vm.time!!
chronometer.start()
btn_fragment_share.setOnClickListener {
findNavController().navigate(R.id.viewModelShareActivity)
}
}
}
代碼很簡單,只是在ViewModel中存儲了一個time
值,在fragment中啓動計時器,當我們旋轉屏幕的時候你會發現,計時器的值並沒有變化,仍然按照旋轉之前的數值進行計數。
3.2 Fragment數據共享
在ViewModelShareActivity中展示了ViewModel中的數據進行Fragment數據共享的功能。
在之前的DemoViewModel中我們存儲了seekbar
的值,然後我們看Fragment中是怎麼實現的?
class DataShareFragment : Fragment() {
private lateinit var mViewModel: DemoViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
mViewModel = ViewModelProviders.of(activity!!).get(DemoViewModel::class.java)
return inflater.inflate(R.layout.fragment_data_share, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
subscribeSeekBar()
}
private fun subscribeSeekBar() {
// 監聽SeekBar改變ViewModel的中的值
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
mViewModel.seekbarValue.value = progress
}
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
// 當ViewModel中的值發生變化時,更新SeekBar
activity?.let {
mViewModel.seekbarValue.observe(it, Observer<Int> { value ->
if (value != null) {
seekBar.progress = value
}
})
}
}
}
看代碼其實挺簡單的,只做了兩個操作:
- 監聽SeekBar改變ViewModel的中的值
- 當ViewModel中的值發生變化時,更新SeekBar
同樣,當旋轉屏幕之後,SeekBar的值也不會改變。到這裏ViewModel的基本使用方式我們已經瞭解了,接下來我們來分析一下它具體是怎麼實現的?
沒錯,又到了源碼分析的部分了,相信很多童鞋也都比較不喜歡源碼的部分,包括我也是,之前很不願意去看源碼,但是當你嘗試看了一些源碼之後,你會發現很有意思的,因爲有些東西你是有必要去分析源碼實現的,這是作爲一個開發者必備的基本的素質,而且當你使用一個組件的時候,一步一步的跟着代碼走,慢慢的分析了整個的組件設計方式,最後站在開發這個組件的角度,去看他的設計思想和一些模式的時候,對自己本身也是一個很大的提高,所以我真的建議有興趣的可以跟着自己的思路一步一步的看下源碼。好了廢話不多說。
4. 源碼分析
ViewModelProviders
在使用VM的時候一般都通過val vm = ViewModelProviders.of(this).get(DemoViewModel::class.java)
去創建,具體來看一下of方法:
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(fragment.getViewModelStore(), factory);
}
of方法通過ViewModelProvider中的一個單例AndroidViewModelFactory創建了Factory幫助我們去創建VM,並且返回了一個ViewModelProvider。大家注意一下第一個參數fragment.getViewModelStore()
,這個ViewModelStore是什麼呢?接着看
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
//存儲VM
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();
}
}
其實就是一個存儲VM的容器,裏面通過HashMap進行存和取。
下面我們看創建VM時的get()
方法
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//在ViewModelStore中拿到VM實例
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
//工廠創建VM,並保存VM
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
get()
方法就是獲取VM實例的過程,如果VM不存在,則通過工廠去創建實例。具體工廠創建實現:
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
/**
* Retrieve a singleton instance of AndroidViewModelFactory.
*
* @param application an application to pass in {@link AndroidViewModel}
* @return A valid {@link AndroidViewModelFactory}
*/
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
/**
* Creates a {@code AndroidViewModelFactory}
*
* @param application an application to pass in {@link AndroidViewModel}
*/
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 {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
}
.......
}
return super.create(modelClass);
}
}
這個工廠就很簡單了,就是通過反射來創建VM實例。
到這裏VM的創建過程就差不多了,而我們發現他並沒有和生命週期有什麼相關的東西,或者說VM是怎樣保證的的數據不被銷燬的呢?看了網上的一些文章,很多都說在of
方法的時候傳入了Fragment,並且通過HolderFragment持有ViewModelStore對象等等……比如:
但是在我這裏發現跟他們的都不一樣,我搜了一下ViewModelStores,發現它已經‘退役’了。
並且它的註釋也告訴了我們它的繼承者:
也就是我們在of()
方法中的:
也就是說谷歌已經將ViewModelStore集成到了Fragment中,一起去看一下吧:
Fragment.java
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
return mFragmentManager.getViewModelStore(this);
}
FragmentManagerImpl.java
private FragmentManagerViewModel mNonConfig;
......
@NonNull
ViewModelStore getViewModelStore(@NonNull Fragment f) {
return mNonConfig.getViewModelStore(f);
}
我們可以看到代碼到了我們熟悉的FragmentManagerImpl,它的裏面持有了一個FragmentManagerViewModel實例,進去看看這個是個什麼東西:
FragmentManagerViewModel.java
class FragmentManagerViewModel extends ViewModel {
//創建FragmentVM的工廠
private static final ViewModelProvider.Factory FACTORY = new ViewModelProvider.Factory() {
@NonNull
@Override
@SuppressWarnings("unchecked")
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
FragmentManagerViewModel viewModel = new FragmentManagerViewModel(true);
return (T) viewModel;
}
};
//從viewModelProvider中獲取FragmentVM實例
@NonNull
static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
FACTORY);
return viewModelProvider.get(FragmentManagerViewModel.class);
}
//非中斷的Fragment集合
private final HashSet<Fragment> mRetainedFragments = new HashSet<>();
private final HashMap<String, FragmentManagerViewModel> mChildNonConfigs = new HashMap<>();
private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();
private final boolean mStateAutomaticallySaved;
// Only used when mStateAutomaticallySaved is true
private boolean mHasBeenCleared = false;
// Only used when mStateAutomaticallySaved is false
private boolean mHasSavedSnapshot = false;
FragmentManagerViewModel(boolean stateAutomaticallySaved) {
mStateAutomaticallySaved = stateAutomaticallySaved;
}
//添加非中斷Fragment
boolean addRetainedFragment(@NonNull Fragment fragment) {
return mRetainedFragments.add(fragment);
}
@NonNull
Collection<Fragment> getRetainedFragments() {
return mRetainedFragments;
}
//是否改銷燬
boolean shouldDestroy(@NonNull Fragment fragment) {
if (!mRetainedFragments.contains(fragment)) {
// Always destroy non-retained Fragments
return true;
}
if (mStateAutomaticallySaved) {
// If we automatically save our state, then only
// destroy a retained Fragment when we've been cleared
return mHasBeenCleared;
} else {
// Else, only destroy retained Fragments if they've
// been reaped before the state has been saved
return !mHasSavedSnapshot;
}
}
boolean removeRetainedFragment(@NonNull Fragment fragment) {
return mRetainedFragments.remove(fragment);
}
//獲取VMStore
@NonNull
ViewModelStore getViewModelStore(@NonNull Fragment f) {
ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
if (viewModelStore == null) {
viewModelStore = new ViewModelStore();
mViewModelStores.put(f.mWho, viewModelStore);
}
return viewModelStore;
}
//銷燬並清空VM
void clearNonConfigState(@NonNull Fragment f) {
if (FragmentManagerImpl.DEBUG) {
Log.d(FragmentManagerImpl.TAG, "Clearing non-config state for " + f);
}
// Clear and remove the Fragment's child non config state
FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);
if (childNonConfig != null) {
childNonConfig.onCleared();
mChildNonConfigs.remove(f.mWho);
}
// Clear and remove the Fragment's ViewModelStore
ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
if (viewModelStore != null) {
viewModelStore.clear();
mViewModelStores.remove(f.mWho);
}
}
看代碼我們發現它繼承了VM,並且裏面保存了VMStore,也就是說保存了VM,同時清空的操作也在這裏面:clearNonConfigState()
ViewModelStore.java
/**
* 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();
}
那麼到底是什麼時候來清空VM的呢?也就是說clear()
是什麼時候調用的呢?查看源碼我們發現有兩處:
- 也就是上面提到的FragmentViewModel.java裏面的
clearNonConfigState()
方法,而這個方法只在一個地方被調用了:
if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
boolean beingRemoved = f.mRemoving && !f.isInBackStack();
if (beingRemoved || mNonConfig.shouldDestroy(f)) {
boolean shouldClear;
if (mHost instanceof ViewModelStoreOwner) {
shouldClear = mNonConfig.isCleared();
} else if (mHost.getContext() instanceof Activity) {
Activity activity = (Activity) mHost.getContext();
shouldClear = !activity.isChangingConfigurations();
} else {
shouldClear = true;
}
//Fragment正在被移除或者應該清空的狀態下
if (beingRemoved || shouldClear) {
mNonConfig.clearNonConfigState(f);
}
f.performDestroy();
dispatchOnFragmentDestroyed(f, false);
} else {
f.mState = Fragment.INITIALIZING;
}
這個方法是在FragmentManagerImpl.java中的
moveToState
方法裏面的,這個方法是跟隨着Fragment的生命週期的,當這個方法被調用時,判斷兩個狀態beingRemoved
和shoudClear
然後調用clear()
方法。關於moveToState()
可以查看這篇文章:Fragment之底層關鍵操作函數moveToState
- 第二個調用的地方就是ComponentActivity.java
getLifecycle().addObserver(new GenericLifecycleObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {//不是改變狀態配置的
getViewModelStore().clear();
}
}
}
});
關於ComponentActivity,如果有看過之前我分析過的關於Lifecycles的應該對它有所瞭解3. Jetpack源碼解析—用Lifecycles管理生命週期。FragmentActivity繼承了它,而ComponentActivity裏面通過Lifecycles觀察生命週期,當接受到ON_DESTROY的事件時,清空VM。
5. 總結
分析完源碼之後,我們來總結一下整個流程:
- 通過
ViewModelProviders.of(this).get(DemoViewModel::class.java)
創建VM of()
方法中傳入fragment.getViewModelStore()
並且返回VMProviders- 查看FragmentManagerImpl中
getViewModelStore()
,持有FragmentManagerViewModel對象 - 在FragmentManagerViewModel中清空VM操作
clearNonConfigState()
,同時在ViewModelStore中clear()
了ViewModel的value值 - 最後我們發現只有在ComponentActivity中觀察到接收到
ON_DESTROY
的事件時同時並不是由於configuration
發生變化時纔會執行clear()
操作;另外一處是在moveToState()
方法中,滿足beingRemoved
和shouldClear
狀態也會清空VM
好了 整個流程就是這樣了,並沒有特別深入的去分析,但是基本的原理我們已經清楚了,Demo中也只是簡單的使用了VM。
JetPack源碼分析系列文章到這裏應該就結束了,後面還有Paging、WorkManager、Room,以及Camera2和ViewPager2,這些目前暫時不分析了,但是也會出基本的使用和簡單的分析,一共5篇源碼分析,文章中的Demo我寫了一個APP—JetpackNote,裏面有基本的Demo例子,和文章的分析;一直也沒有提到是因爲功能還不完善,我會盡快完善它的,也希望有什麼意見的小夥伴可以和我溝通交流。
Github源碼地址在: