Android-ViewModel探究

ViewModel如何产生的?

通常我们第一步都是通过ViewModelProviders.of()这个工具方法进行传入activity来获得。我们直接看源码

传入FragmentActivity

1
2
3
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}

走到重载方法

1
2
3
4
5
6
7
8
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);
}

这两个方法区别就是有无提供工厂,其实实际上ViewModel的生成就是工厂生产出来的。这里的话如果不提供工厂则会给我们一个默认的工厂。最后构建一个ViewModelProvider

1
2
3
4
5
6
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}

默认工厂也很简单就是一个AndroidViewModelFactory实例

我们回头看ViewModelProvider的构造方法

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

传入一个ViewModelStoreFactory。这个ViewModelStore看名字就是实际存储用于存储ViewModel的。而它也是直接从FragmentActivity.getViewModelStore()获得。

目前我们就获得到了一个activity的ViewModelProvider。它维持了一个FactoryViewModelStore,分别用于生产ViewModel和存储ViewModel

下一步,获取具体的ViewModelViewModelProvider.get(clazz)

1
2
3
4
5
6
7
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);
}

通过key获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);//获取viewmodel

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);//放入ViewModelStore
//noinspection unchecked
return (T) viewModel;//返回
}

看到这里就很明确了,首先直接取ViewModelStore中通过key查找ViewModel,查找到了且类型正确就返回。不存则通过工厂进行创建,并且放入ViewModelStore中,这样就完成一个拿去ViewModel

然后看下ViewModelStore的实现,一个很简单的HashMap存储

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

看下工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static class NewInstanceFactory implements Factory {

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

这里调下默认最简单的工厂,就是直接反射创建实例,而AndroidViewModelFactory是它的子类,只是多了一个传入Application参数的功能,其它都没什么。

走到这里也大概明白的ViewModel的创建。我们平时使用中两个fragment可以共享activityviewmodel,也正式因为通过activityViewModelStore进行获取的,也就完成了ViewModel局部单例的效果。这个效果基本上是靠ViewModelProvider(xxx.getViewModelStore(), factory)传入不同的ViewModelStore完成的。

一个activity可以有多个fragment,通过设置activity的ViewModelStore完成局部单例viewmdoel。换句话说只要设置是同一个ViewModelStore在这个范围就是单例的。Fragment的ViewModelStore则是实际来源于FragmentManager的。

全局单例ViewModel

有这样的场景,有一个view数据基本在全局都会使用的到,这个时候我们就要实现一个全局的ViewModel,这个时候我们可以让Application来实现ViewModelStoreOwner提供出一个全局的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
class ViewModelFactory private constructor(private val dataRepository: DataRepository) :
ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>) =
with(modelClass) {
when {
isAssignableFrom(LocalMusicViewModel::class.java) ->
LocalMusicViewModel(dataRepository)
else -> throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
}
} as T

companion object {
@Volatile
private var INSTANCE: ViewModelFactory? = null
fun getInstance(application: Application) =
INSTANCE ?: synchronized(ViewModelFactory::class.java) {
INSTANCE
?: ViewModelFactory(Injection.provideDataRepository(application.applicationContext))
.also { INSTANCE = it }
}

}

}