Jetpack-ViewModel简析

源码分析基于androidx版本

ViewModel原理分析

ViewModel简介

ViewModel是用来存储和管理Lifecycle创建数据的组件,在配置发生改变或者屏幕旋转时数据仍然不会丢失。ViewModel可以负责组件间的通信,可以高效解决Activity与Fragment的通信问题。

主要的功能还是在非手动关闭以及系统回收条件下进行 Activity/Fragment的数据保存。

具有以下优势:

  • 与UI层低耦合
  • 可以在Fragment间共享ViewModel
  • 数据一致保存在内存中,即使Activity重建

ViewModel使用示例

系统默认提供两种ViewModel

  • ViewModel:抽象类实现
  • AndroidViewModel:相对于ViewModel多提供一个application的入参,可以通过application获取资源或者应用信息。

ViewModel绝不能引用View、Lifecycle或者Activity上下文引用的任何类。

build.gradle引入ViewModel

1
2
3
4
dependencies {
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.1.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
}

创建一个类实现ViewModel/AndroidViewModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyViewModel : ViewModel() {
private val users: MutableLiveData<List<User>> by lazy {
MutableLiveData().also {
loadUsers()
}
}

fun getUsers(): LiveData<List<User>> {
return users
}

private fun loadUsers() {
// Do an asynchronous operation to fetch users.
}
}


class XXAndroidViewModel : AndroidViewModel(MyApp.getContext()){

}

创建完成后,需要在Activity/Fragment中调用

1
2
3
4
5
6
7
8
9
10
class MyActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
model.getUsers().observe(this, Observer<List<User>>{ users ->
// update UI
})
}
}

上述为最简单的使用示例。

ViewModel源码解析

源码分析

ViewModel-结构

ViewModel-结构

ViewModel

抽象类,主要提供onCleared()可以清理一些自定义数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public abstract class ViewModel {
//自定义清理
protected void onCleared() {
}

@MainThread
final void clear() {
mCleared = true;
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
//针对 协程作用域的关闭
closeWithRuntimeException(value);
}
}
}
// 清理ViewModel相关数据
onCleared();
}
}

还提供了AndroidViewModel,支持传入Application

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;

//添加对 Application的支持
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}

/**
* Return the application.
*/

@SuppressWarnings("TypeParameterUnusedInFormals")
@NonNull
public <T extends Application> T getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}

ViewModelProvider

为Activity、Fragment提供ViewModel的工具类,主要是创建ViewModel

ViewModel-ViewModelProvider

内部参数
1
2
3
4
5
6
7
8
9
public class ViewModelProvider {
//创建ViewModel的工厂
private final Factory mFactory;
//存储ViewModel对象
private final ViewModelStore mViewModelStore;
//构建ViewModel对象 默认key,在ViewModelStore根据key获取ViewModel
private static final String DEFAULT_KEY =
"androidx.lifecycle.ViewModelProvider.DefaultKey";
}
构造方法
1
2
3
4
5
6
7
8
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}

提供两个构造方法,最终还是从ViewModelStore获取缓存的ViewModel

获取ViewModel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
//创建ViewModel
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中获取ViewModel数据

Factory

Factory主要用来创建ViewModel

1
2
3
4
public interface Factory {
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

系统提供的Factroy主要有以下几类

  • NewInstanceFactory:通过Class.newInstance()直接创建ViewModel实例
  • AndroidViewModelFactory:针对AndroidViewModel,通过Class.getConstructor()传入Application实例

ViewModelStore

内部主要缓存ViewModel

ViewModel-ViewModelStore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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());
}

//当内部ViewModel不使用时,及时清理内存
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}

内部主要维护一个mMap,缓存ViewModel。

ViewModelStoreOwner
1
2
3
4
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}

是一个接口,表示ViewModelStore的作用域,主要实现类为ComponentActivityandroidx.fragment.app.Fragment

ViewModel-生命周期绑定

ViewModel-生命周期绑定

ViewModel对象存在的时间范围是获取ViewModel时传递给ViewModelProviderLifecycleViewModel通过LifecycleActivity/Fragment进行绑定的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//ComponentActivity.java
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event)
{
//当Activity结束的时候,销毁ViewModel
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
//ViewModel销毁
getViewModelStore().clear();
}
}
}
});

说明 ViewModel 随着 Activity 状态的改变而经历的生命周期。

ViewModel-Activity销毁重建

ViewModel-Activity销毁重建过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//ComponentActivity.java
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;
}

当Activity创建时,并且调用ViewModelProvider().get(XXModel::class.java)时,会调用到getViewModelStore()此时会分为两种情况:

  • getLastNonConfigurationInstance()不为null,直接从内部获取ViewModelStore
  • getLastNonConfigurationInstance()为null,新建一个ViewModelStore对象

如何判断getLastNonConfigurationInstance()是否为null,就需要找到写入该数据的地方,判断在什么场景下会执行写入?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();

ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}

if (viewModelStore == null && custom == null) {
return null;
}

NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}

ComponentActivity重写了onRetainNonConfigurationInstance()方法,在其中存储了ViewModelStore

为什么NonConfiguationInstance可以在Activity重建时不被销毁?

Activity重建时的生命周期过程:onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume

那就需要在onDestroy时,存储NonConfigurationInstances,才可以在后续启动时重新获取到数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//ActivityThread.java
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason)
{
ActivityClientRecord r = mActivities.get(token);
//默认 getNonConfigInstance为true
if (getNonConfigInstance) {
try {
//将写入的 NonConfigurationInstances 存到ActivityClientRecord中
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to retain activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Activity.java
NonConfigurationInstances retainNonConfigurationInstances() {
//执行到 ComponentActivity.onRetainNonConfigurationInstance() ,返回了携带ViewModelStore的NonConfigurationInstances
Object activity = onRetainNonConfigurationInstance();

HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
...
ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();


NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;

return nci;
}

Activity重启时

1
2
3
4
5
6
7
8
9
10
//ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances/*ActivityClientRecord获取的数据*/, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Activity.java
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken)
{
...
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);

//赋值 mLastNonConfigurationInstances
mLastNonConfigurationInstances = lastNonConfigurationInstances;
}

这样就回到了ComponentActivitygetViewModelStore()时,就可以获取写入的NonConfigurationInstances

由于NonConfigurationInstances是写入到ActivityClientRecord中,与Activity生命周期无关。

ViewModel-Fragment销毁重建

ViewModel-Fragment销毁重建过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//androidx.fragment.app.Fragment.java    
FragmentManagerImpl mFragmentManager;

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);
}

mNonConfig是一个FragmentManagerViewModel类型,意味着也是ViewModel的一种,对应的需要找到初始化的地方。

1
2
3
4
5
6
7
8
9
10
11
//FragmentManagerViewModel.java
class FragmentManagerViewModel extends ViewModel {
...
//需要 Retain的Fragment
private final HashSet<Fragment> mRetainedFragments = new HashSet<>();
//存储了childFragment的 FragmentManagerViewModel
private final HashMap<String, FragmentManagerViewModel> mChildNonConfigs = new HashMap<>();
//存储了childFragment的 ViewModelStore
private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();

}

Fragment嵌套-ViewModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void attachController(@NonNull FragmentHostCallback host,
@NonNull FragmentContainer container, @Nullable final Fragment parent)
{
...
if (parent != null) {
//当前为childFragment
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) {
//当前为 FragmentActivity
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
} else {
//都不是 就直接新建ViewModel对象
mNonConfig = new FragmentManagerViewModel(false);
}
}

接下来寻找attachController的调用

分为两处调用,一处位于Fragment,另一处位于FragmentController实际就是FragmentActivity

1
2
3
4
5
6
//Fragment.java
void performAttach() {
mChildFragmentManager.attachController(mHost, new FragmentContainer() {...}, this);
mCalled = false;
onAttach(mHost.getContext());
}

当从Fragment获取数据时,数据来源是getChildNonconfig()

1
2
3
4
5
//FragmentActivity.java
protected void onCreate(@Nullable Bundle savedInstanceState) {
mFragments.attachHost(null /*parent*/);
...
}

此时初始化完成FragmentManagerViewModel并存入到FragmentActivityViewModelStore中。

ViewModel-Fragment

总结

Fragment声明的ViewModel存储在FragmentManagerImpl.mNonConfig实际存储于FragmentManagerViewModel中,而FragmentManagerViewModel实际存储于FragmentActivity.mViewModelStore中。

所以ViewModel在Fragment中也不会因为重建而被销毁。

原因就是Fragmen的ViewMode存储于FragmentManagerViewModel,而它存在于FragmentActivity的ViewModelStore中。FragmentActivity的ViewModelStore会在Activity销毁时存储于NonConfigurationInstances中,当然也会存到ActivityyClienrtRecord中。

ViewModel-Fragment间数据共享

ViewModel-Fragment间数据共享

Fragment之间使用Activity范围共享的ViewModel处理Fragment间的数据共享。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//定义一个共享的ViewModel
class SharedViewModel : ViewModel() {
val selected = MutableLiveData<Item>()

fun select(item: Item) {
selected.value = item
}
}


class OneFragment : Fragment() {

private lateinit var itemSelector: Selector

private val model: SharedViewModel = ViewModelProviders.of(activity).get(ShareViewModel::class.java)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
}

class TwoFragment : Fragment() {

private val model: SharedViewModel = ViewModelProviders.of(activity).get(ShareViewModel::class.java)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
// Update the UI
})
}
}

当两个Fragment获取ViewModel时,都会从宿主Activity的ViewModelStore获取数据,又因为key一致,所以可以得到同一个ViewModel对象。

只要保证作用域(ViewModelStore)相同,就可以获取相同的ViewModel实例

ViewModel拓展

ViewModel与协程

Android 提供了viewModelScope这一拓展属性,可以在ViewModel销毁时退出,可以利用viewModelScope启动ViewModel

中的协程,而不用担心任务泄漏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//示例代码
class MyViewModel() : ViewModel() {

fun initialize() {
viewModelScope.launch {
processBitmap()
}
}

suspend fun processBitmap() = withContext(Dispatchers.Default) {
// 在这里做耗时操作
}

}
1
2
3
4
5
6
7
8
9
10
11
12
//ViewModel.kt
public val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(
JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
)
}

Activity销毁重建数据恢复方式

ViewModel-Activity重建恢复数据方式

onSaveInstanceState() / onRestoreInstanceState()

当Activity开始停止时,系统会调用onSaveInstanceState()存储当前Activity的状态,执行于onStop()之后。

重建先前被销毁的Activity时,调用到onRestoreInstanceState()获取原先存储的Activity状态,在onCreate()也会获得同样的数据。数据对象是Bundle

Fragment.setRetainInstance(true)

当配置发生改变时,Fragment 会随着宿主 Activity 销毁与重建,当我们调用 Fragment 中的 setRetainInstance(true) 方法时,系统允许 Fragment 绕开销毁-重建的过程。使用该方法,将会发送信号给系统,让 Activity 重建时,保留 Fragment 的实例。需要注意的是:

  • 使用该方法后,不会调用 Fragment 的 onDestory()方法,但仍然会调用 onDetach() 方法
  • 使用该方法后,不会调用 Fragment 的 onCreate(Bundle) 方法。因为 Fragment 没有被重建。
  • 使用该方法后,Fragment 的 onAttach(Activity)onActivityCreated(Bundle) 方法仍然会被调用。

简述setRetainInstance(true)流程:

调用setRetainInstance(true)时,会将该Fragment存入到FragmentManagerViewModel中的mRetainedFragment中,等价于Fragment已经位于FragmentActivityViewModelStore中。也就是存储于NonconfigurationInstance中。

后续可以从mLastConfiguationInstances获取存储的Fragment,再调用到FragmentManager.restoreSaveState()就可以还原Fragment。Fragment实例也没有发生改变。

onRetainNonConfigurationInstance()/getLastNonConfigurationInstance()

在 Activity 中提供了 onRetainNonConfigurationInstance 方法,用于处理配置发生改变时数据的保存。随后在重新创建的 Activity 中调用 getLastNonConfigurationInstance 获取上次保存的数据。我们不能直接重写上述方法,如果想在 Activity 中自定义想要恢复的数据,需要我们调用上述两个方法的内部方法:

  • onRetainCustomNonConfigurationInstance()
  • getLastCustomNonConfigurationInstance()

注意:onRetainNonConfigurationInstance 方法系统调用时机介于 onStop - onDestory 之间,getLastNonConfigurationInstance 方法可在 onCreate 与 onStart 方法中调用。

存储位置 数据类型 系统内存不足回收
数据是否存在
配置更改
数据是否存在
数据读写速度
onSaveInstanceState() /
onRestoreInstanceState()
序列化到磁盘 支持基础数据类型可序列化对象
有IO操作
setRetainInstance(true) 内存 支持复杂对象
受可用内存限制

实例已被销毁

直接内存读写
onRetainNonConfigurationInstance()/
getLastNonConfigurationInstance()
内存 支持复杂对象
受可用内存限制

实例已被销毁

直接内存读写

参考链接

ViewModel知识点

AndroidDeveloper-ViewModel


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!