先看一个ViewModel的使用例子,这里使用Kotlin语言(读者不该困惑于语言,主要是看用法,Java的类似,不懂欢迎留言):
class ShareViewModel : AndroidViewModel {
private val userLiveData:MutableLiveData<User> = MutableLiveData()
constructor(application: Application) : super(application){
userLiveData.value = User("default","123")
}
fun getUserLiveData() : MutableLiveData<User>{
return userLiveData
}
}
mShareViewModel = ViewModelProviders.of(activity!!).get(ShareViewModel::class.java)
mShareViewModel.getUserLiveData().observe(this, Observer {
mTvUserName.text = it.username
})
首先我们需要创建一个ViewModel,在这个ViewModel里面处理与UI操作无关的业务逻辑,我们这里的demo为了简化只是管理了一个LiveData,如果读者不了解LiveData,可以点击这里:【Android AAC】第二篇 LiveData的源码解析。
通过以上两步我们的ViewModel就可以通过LiveData连接User(Model)和UI(Activity/Fragment)了,可以在不同的Fragment共享这个ViewModel,从而共享数据。
下面我们通过分析ViewModel的源码来分析ViewModel的工作原理。
ViewModel的源码很简单,只有一个方法
ViewModel.java
public abstract class ViewModel {
/**
* This method will be called when this ViewModel is no longer used and will be destroyed.
*
* It is useful when ViewModel observes some data and you need to clear this subscription to
* prevent a leak of this ViewModel.
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}
AndroidViewModel.java
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
/**
* Return the application.
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
@NonNull
public <T extends Application> T getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}
AndroidViewModel和ViewModel的源码都很少,ViewModel被设计用来承载与UI无关的业务代码,同时它可能在不同的Fragment之间共享,那么我们从这一点入手,看看它是怎么实现数据共享的。
ViewModel的创建代码如下:
ViewModelProviders.of(activity!!).get(ShareViewModel::class.java)
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
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的创建过程为:ViewModelProviders.of -> ViewModelProvider instance -> ViewModelProvider instance . get(),ViewModelProviders
类似一个工具类,我们看看ViewModelProvider.java
的构造方法
/**
* Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
* {@code Factory} and retain them in a store of the given {@code ViewModelStoreOwner}.
*
* @param owner a {@code ViewModelStoreOwner} whose {@link ViewModelStore} will be used to
* retain {@code ViewModels}
* @param factory a {@code Factory} which will be used to instantiate
* new {@code ViewModels}
*/
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
/**
* Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
* {@code Factory} and retain them in the given {@code store}.
*
* @param store {@code ViewModelStore} where ViewModels will be stored.
* @param factory factory a {@code Factory} which will be used to instantiate
* new {@code ViewModels}
*/
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
ViewModelProvider
的创建依赖于ViewModelStore
和Factory
,ViewModelStore是一个维护了一个String和ViewModel映射表的类,用来管理ViewModel,在ViewModel不再使用的时候调用它的onCleared()
方法释放资源。而Factory
是一个接口
ViewModelStore.java
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);
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
Factory.java
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
*
*
* @param modelClass a {@code Class} whose instance is requested
* @param The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
我们看看我们在Activity中创建ViewModel时传入的ViewModelStore
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);
}
FragmentActivity.java -> getViewModelStore(),在这个方法中先是判断是不是由于Activity配置发生改变,如果是配置发生改变就恢复ViewModelStore,否则会创建一个ViewModelStore实例保存在Activity成员变量中,一个Activity实例只会有一个ViewModelStore对象。
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;
}
下面我们看看我们传入的Factory
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
AndroidViewModelFactory.java
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);
} 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);
}
}
AndroidViewModelFactory继承于ViewModelProvider.NewInstanceFactory ,重写了create
方法,在方法里面判断了我们ViewModel的class类型,如果是AndroidViewModel的子类,那么就会调用AndroidViewModel的Application参数的构造方法,否则调用父类的create
方法实现。
NewInstanceFactory.java
/**
* Simple factory, which calls empty constructor on the give class.
*/
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);
}
}
}
它父类的create
方法中直接通过反射调用ViewModel的一个参数的构造方法创建ViewModel实例。
Tips:
ViewModelProvider
类生成之后调用它的get方法获得ViewModel
实例,下面再来看看它的get
方法
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);
}
方法中首先判断了我们传入的class是不是匿名内部类的class,匿名内部类不允许作为ViewModel。然后通过约定的KEY前缀 + 类名组合成KEY调用重载的get
方法。
@NonNull
@MainThread
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 = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
可以看到在这里先判断ViewModelStore
是否有ViewModel,如果有则直接返回,否则通过Factory的create
方法创建了ViewModel后保存在了ViewModelStore
中,意味着如果ViewModelStore
和modelClass
一样的话多次调用ViewModelProvider
的get方法将会获取到同一个ViewModel,所以,我们在Fragment中共享ViewModel只需要让Fragment依附在同一个Activity中并且通过Activity来获取ViewModel,同一个Activity的ViewModelStore
是一样的。
到这里我们知道了ViewModel的创建过程,那么ViewModel的onCleared
方法是什么时候调用的呢,也就是我们在onCleared
中释放的资源操作何时会执行?
细心的朋友也许发现了,刚我们在看ViewModelStore
源码的时候,看到它有一个clear()
方法
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
我们跟踪一下clear()
方法的调用发现在FragmentActivity
的onDestroy()
中被调用
@Override
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
mFragments.dispatchDestroy();
}
也就是说在Activity销毁的时候系统会自动调用ViewModel的onCleared()
方法,因此我们可以在onCleared()
中释放我们的资源。
上面所说的是在Activity中创建ViewModel,那么在Fragment中创建ViewModel呢?
我们看看ViewModelProviders
的of
的另一个重载方法
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);
}
Fragment -> getViewModelStore()
public ViewModelStore getViewModelStore() {
if (getContext() == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
return mViewModelStore;
}
可以看到,在Fragment的实例成员中也维护了一个ViewModelStore
,所以在不同的Fragment通过该方法获得同一个ViewModel类的实例不一样。
之前说我们在不同的Fragment共享ViewModel可以通过Activity来获取ViewModel的方式共享,那么如果这两个Fragment在不同的Activity中呢?或是两个Activity要共享ViewModel呢?我们该如何实现ViewModel共享?
通过前面的源码分析我们可以得出结论,只需要保证ViewModelStore
一样,那么同一个ViewModel类会获取同一个实例,那么我们可以在Application中自己维护一个ViewModelStore
实例,通过这个ViewModelStore
创建ViewModelProvider
完成ViewModel
的不同Activity
之间或者不同Fragment
的ViewModel
共享。
下面是不同Activity共享ViewModel的demo(Kotlin):
App.java
class App : Application() {
private lateinit var mViewModelStore:ViewModelStore
override fun onCreate() {
super.onCreate()
mViewModelStore = ViewModelStore()
}
fun getViewModelStore() : ViewModelStore {
return mViewModelStore
}
}
MainActivity.java
class MainActivity : AppCompatActivity() {
private lateinit var mTvUserName: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mTvUserName = findViewById(R.id.tv_username)
if(application is App){
val shareViewModel = ViewModelProvider(
(application as App).getViewModelStore(),
ViewModelProviders.DefaultFactory(application)
).get(
ShareViewModel::class.java
)
shareViewModel.getUserLiveData().observe(this, Observer {
mTvUserName.text = "name:${it.username}"
})
}
}
fun second(view: View){
startActivity(Intent(this,SecondActivity::class.java))
}
}
SecondActivity.java
class SecondActivity : AppCompatActivity(), TextWatcher {
private lateinit var mShareViewModel:ShareViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
var et_username: EditText = findViewById(R.id.et_username)
et_username.addTextChangedListener(this)
if(application is App){
mShareViewModel = ViewModelProvider(
(application as App).getViewModelStore(),
ViewModelProviders.DefaultFactory(application)
).get(
ShareViewModel::class.java
)
}
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun afterTextChanged(p0: Editable) {
Log.e("SecondActivity","afterTextChanged/p0:$p0")
mShareViewModel.getUserLiveData().value?.username = p0.toString()
mShareViewModel.getUserLiveData().value = mShareViewModel.getUserLiveData().value
}
}