Android AAC第三篇 ViewModel源码解析
Posted 宿罪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android AAC第三篇 ViewModel源码解析相关的知识,希望对你有一定的参考价值。
前言
先看一个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.
* <p>
* 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的创建过程
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.
* <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);
我们看看我们在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:
- 如果你使用的是ViewModel,即直接继承ViewModel,那么必须提供一个无参数的构造方法,否则运行的时候会报错哦。
- 我们也可以自己创建Factory(实现Factory接口即可)来改变创建ViewModel的方式。
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):
不同Activity间共享ViewModel
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.getUse以上是关于Android AAC第三篇 ViewModel源码解析的主要内容,如果未能解决你的问题,请参考以下文章