本文使用的版本为ViewModel2.6.0-alpha02,API一直在改变,因此要注意下版本。
接上篇,来看看为什么在Activity执行onCreate 之前初始化ViewModel会报错。
Caused by: java.lang.IllegalStateException: Your activity is not yet attached to the Application instance. You can't request ViewModel before onCreate call.
在理解ViewModel之前,最好对工厂模式有一定的了解。理解设计模式,可以更好地方便理解源码。
可参考这篇,这是我看过的讲设计模式讲的很好的一个博客。
https://blog.csdn.net/LoveLion/article/details/17517213
ViewModel这里用到的是抽象工厂模式。
https://blog.csdn.net/lovelion/article/details/9319181
工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑 将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产。
抽象工厂模式结构
在抽象工厂模式结构图中包含如下几个角色:
● AbstractFactory(抽象工厂):它声明了一组用于创建一族产品的方法,每一个方法对应一种产品。
● ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
● AbstractProduct(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
● ConcreteProduct(具体产品):它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。
抽象工厂典型代码如下
abstract class AbstractFactory {
//工厂方法一
public abstract AbstractProductA createProductA();
//工厂方法二
public abstract AbstractProductB createProductB();
……
}
具体工厂类,其典型代码如下
class ConcreteFactory1 extends AbstractFactory {
//工厂方法一
public AbstractProductA createProductA() {
return new ConcreteProductA1();
}
//工厂方法二
public AbstractProductB createProductB() {
return new ConcreteProductB1();
}
……
}
ViewModel,这是一个抽象类。可以看做一个抽象产品。
//ViewModel
public abstract class ViewModel {
/**
* Construct a new ViewModel instance.
* <p>
* You should <strong>never</strong> manually construct a ViewModel outside of a
* {@link ViewModelProvider.Factory}.
*/
public ViewModel() {
}
}
当定义一个简单的不带参数的StringViewModel,继承自ViewModel。 这就是一个具体产品。
class StringViewModel: ViewModel(){
var stringLiveData: MutableLiveData<String> = MutableLiveData()
fun updateStringData(){
stringLiveData.postValue("AAA")
}
}
然后调用ViewModelProvider(this).get(StringViewModel::class.java)
。
class MainActivity : AppCompatActivity() {
// 使用 Activity 库 提供的 viewModels() 扩展函数
// private val stringViewModel: StringViewModel by viewModels()
//错误的写法 这种要写在onCreate里面。或者用var定义,在onCreate中初始化,但是kotlin一般少使用var。
private val stringViewModel: StringViewModel = ViewModelProvider(this).get(StringViewModel::class.java)
.....
}
下面分别来看看ViewModelProvider(this)和get(StringViewModel::class.java)。
1.ViewModelProvider(this)
通过调用ViewModelProvider(this),创建ViewModelProvider
。
调用ViewModelProvider(this)
后,走到ViewModelProvider中。调用次构造函数public constructor( owner: ViewModelStoreOwner )
,然后调用this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
,也就是,走到用@JvmOverloads声明的主构造函数constructor( private val store: ViewModelStore, private val factory: Factory, private val defaultCreationExtras: CreationExtras = CreationExtras.Empty, )
// androidx.lifecycle.ViewModelProvider
public open class ViewModelProvider
@JvmOverloads
constructor(
private val store: ViewModelStore,
private val factory: Factory,
private val defaultCreationExtras: CreationExtras = CreationExtras.Empty,
) {
public interface Factory {
public fun <T : ViewModel> create(modelClass: Class<T>): T {
throw UnsupportedOperationException(
"Factory.create(String) is unsupported. This Factory requires " +
"`CreationExtras` to be passed into `create` method."
)
}
public fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T =
create(modelClass)
...
}
public constructor(
owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
@MainThread
public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
val canonicalName = modelClass.canonicalName
?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
return get("$DEFAULT_KEY:$canonicalName", modelClass)
}
...
}
三个参数,第三个参数CreationExtras 默认赋值为CreationExtras.Empty,重点看前两个参数,ViewModelStore和Factory。
private val store: ViewModelStore,
private val factory: Factory,
private val defaultCreationExtras: CreationExtras = CreationExtras.Empty,
1.1 ViewModelStore
ViewModelStore中存在一个HashMap,存储了ViewModel。
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();
}
}
由于MainActivity 继承自ComponentActivity,因此调用owner.viewModelStore
实际是ComponentActivity中调用getViewModelStore(),最终返回的是mViewModelStore。
// androidx.activity.ComponentActivity
private ViewModelStore mViewModelStore;
/**
* Returns the {@link ViewModelStore} associated with this activity
* <p>
* Overriding this method is no longer supported and this method will be made
* <code>final</code> in a future version of ComponentActivity.
*
* @return a {@code ViewModelStore}
* @throws IllegalStateException if called before the Activity is attached to the Application
* instance i.e., before onCreate()
*/
// 如果在 Activity 附加到应用程序之前调用 抛出异常,例如 在onCreate()之前。
@NonNull
@Override
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.");
}
ensureViewModelStore();
return mViewModelStore;
}
void ensureViewModelStore() {
...
mViewModelStore = new ViewModelStore();
...
}
看到这里,就知道了在 Activity执行onCreate 之前初始化ViewModel为什么会报错 这个问题的答案了。因为getViewModelStore()的时候,判断getApplication() == null
,如果为null,就抛出异常
"Your activity is not yet attached to the " + "Application instance. You can't request ViewModel before onCreate call."
// frameworks/base/core/java/android/app/Activity.java
public final Application getApplication() {
return mApplication;
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
final void attach(
...
Application application, Intent intent,
...) {
...
mToken = token;
...
mApplication = application;
...
}
// frameworks/base/core/java/android/app/ActivityThread.java
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
...
java.lang.ClassLoader cl = appContext.getClassLoader();
//通过反射加载目标Activity,由于我自定义的是MainActivity,所以这里创建了MainActivity。
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
//创建Application
Application app = r.packageInfo.makeApplicationInner(false, mInstrumentation);
...
//调用attach,初始化Activity中的mApplication
activity.attach(..., app, ...);
...
// 开始执行目标Activity的onCreate()方法回调
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
}
//Activity.java
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
...
//由于Java的多态机制,MainActivity(自定义的activity)中重写了onCreate,因此调用的是MainActivity中的onCreate。
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
onCreate(icicle);
}
...
}
Activity启动过程中,调用performLaunchActivity,通过attach,初始化Activity中的mApplication ,调用流程为
performLaunchActivity
->newActivity
->attach
->mInstrumentation.callActivityOnCreate()
->Activity.performCreate()
->MainActivity.onCreate()
最终会回调到了MainActivity(自定义的activity)的onCreate。因此,在onCreate之后,才能确保mApplication 存在。newActivity之后,attach之前,activity中的mApplication 还未初始化。
但是为什么要这样设计?ViewModel用到了mApplication吗?带着这个疑问往下看。
1.2 Factory
ViewModelProvider中定义了一个Factory :public interface Factory
,这是抽象工厂。create函数用于创建具体产品。
/**
* Implementations of `Factory` interface are responsible to instantiate ViewModels.
*/
public interface Factory {
/**
* Creates a new instance of the given `Class`.
*
* Default implementation throws [UnsupportedOperationException].
*
* @param modelClass a `Class` whose instance is requested
* @return a newly created ViewModel
*/
public fun <T : ViewModel> create(modelClass: Class<T>): T {
throw UnsupportedOperationException(
"Factory.create(String) is unsupported. This Factory requires " +
"`CreationExtras` to be passed into `create` method."
)
}
public fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T =
create(modelClass)
...
}
ViewModelProvider源码内部也提供了两个默认Factory实现:NewInstanceFactory
和AndroidViewModelFactory
,这2个是 具体工厂。
1.2.1 defaultFactory(owner)
这里defaultFactory(owner)
创建了一个SavedStateViewModelFactory
,实际创建了AndroidViewModelFactory
,因为ComponentActivity实现了HasDefaultViewModelProviderFactory。
// androidx.lifecycle.ViewModelProvider
public constructor(
owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
//ViewModelProvider.AndroidViewModelFactory.Companion#defaultFactory
//获取默认工厂,如果owner实现了HasDefaultViewModelProviderFactory,则用返回defaultViewModelProviderFactory中的工厂,否则用NewInstanceFactory。
internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
if (owner is HasDefaultViewModelProviderFactory)
owner.defaultViewModelProviderFactory else instance
instance是NewInstanceFactory。
/**
* @suppress
* Retrieve a singleton instance of NewInstanceFactory.
*
* @return A valid [NewInstanceFactory]
*/
@JvmStatic
public val instance: NewInstanceFactory
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
get() {
if (sInstance == null) {
sInstance = NewInstanceFactory()
}
return sInstance!!
}
getDefaultViewModelProviderFactory创建了AndroidViewModelFactory。
//androidx.activity.ComponentActivity#getDefaultViewModelProviderFactory
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
//androidx.lifecycle.SavedStateViewModelFactory
class SavedStateViewModelFactory : ViewModelProvider.OnRequeryFactory, ViewModelProvider.Factory {
@SuppressLint("LambdaLast")
constructor(application: Application?, owner: SavedStateRegistryOwner, defaultArgs: Bundle?) {
...
factory = if (application != null) getInstance(application)
else ViewModelProvider.AndroidViewModelFactory()
}
}
//ViewModelProvider.AndroidViewModelFactory.Companion#getInstance
@JvmStatic
public fun getInstance(application: Application): AndroidViewModelFactory {
if (sInstance == null) {
sInstance = AndroidViewModelFactory(application)
}
return sInstance!!
}
// androidx.lifecycle.ViewModelProvider
@Suppress("SingletonConstructor")
public constructor(application: Application) : this(application, 0)
// androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory
public open class AndroidViewModelFactory
private constructor(
private val application: Application?,
// parameter to avoid clash between constructors with nullable and non-nullable
// Application
@Suppress("UNUSED_PARAMETER") unused: Int,
) : NewInstanceFactory()
这里需要用到getApplication,因此需要保证getApplication不为null。
这里会创建SavedStateViewModelFactory,这里会创建一个AndroidViewModelFactory。
由于AndroidViewModelFactory继承自 public interface Factory
,这里重写了create,当具体工厂需要生产具体产品的时候,会需要调用create,然后就会创建具体产品了,例如AndroidViewModel。
//ViewModelProvider.AndroidViewModelFactory#create
// 调用带Application参数的构造函数创建AndroidViewModel
@Suppress("DocumentExceptions")
private fun <T : ViewModel> create(modelClass: Class<T>, app: Application): T {
return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
try {
modelClass.getConstructor(Application::class.java).newInstance(app)
} catch (e: NoSuchMethodException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: IllegalAccessException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: InstantiationException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: InvocationTargetException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
}
} else super.create(modelClass)
}
//androidx.lifecycle.AndroidViewModel
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
/**
* Return the application.
*/
@SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
@NonNull
public <T extends Application> T getApplication() {
return (T) mApplication;
}
}
2.get(StringViewModel::class.java)
调用get(StringViewModel::class.java)返回了自定义的ViewModel。
// 使用不带参数的ViewModel
class StringViewModel: ViewModel(){
var stringLiveData: MutableLiveData<String> = MutableLiveData()
fun updateStringData(){
stringLiveData.postValue("AAA")
}
}
// androidx.lifecycle.ViewModelProvider
@MainThread
public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
val canonicalName = modelClass.canonicalName
?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
return get("$DEFAULT_KEY:$canonicalName", modelClass)
}
@Suppress("UNCHECKED_CAST")
@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
val viewModel = store[key]
if (modelClass.isInstance(viewModel)) {
(factory as? OnRequeryFactory)?.onRequery(viewModel)
return viewModel as T
} else {
@Suppress("ControlFlowWithEmptyBody")
if (viewModel != null) {
// TODO: log a warning.
}
}
val extras = MutableCreationExtras(defaultCreationExtras)
extras[VIEW_MODEL_KEY] = key
// AGP has some desugaring issues associated with compileOnly dependencies so we need to
// fall back to the other create method to keep from crashing.
return try {
factory.create(modelClass, extras)
} catch (e: AbstractMethodError) {
factory.create(modelClass)
}.also { store.put(key, it) }
}
这里的参数factory,是前面调用defaultFactory(owner)创建的,也就是AndroidViewModelFactory。最终会走到AndroidViewModelFactory.create
// androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory#create(java.lang.Class<T>, android.app.Application)
@Suppress("DocumentExceptions")
private fun <T : ViewModel> create(modelClass: Class<T>, app: Application): T {
// 如果modelClass继承自AndroidViewModel,调用带Application参数的构造函数创建ViewModel对象;
return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
try {
modelClass.getConstructor(Application::class.java).newInstance(app)
} catch (e: NoSuchMethodException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: IllegalAccessException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: InstantiationException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: InvocationTargetException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
}
// 否则调用NewInstanceFactory.create
} else super.create(modelClass)
}
//androidx.lifecycle.ViewModelProvider.NewInstanceFactory#create
@Suppress("DocumentExceptions")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return try {
// 直接反射Class对象的无参构造函数来创建ViewModel
modelClass.newInstance()
} catch (e: InstantiationException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
} catch (e: IllegalAccessException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
}
}
由于自定义的StringViewModel,继承自ViewModel。因此最终调用NewInstanceFactory#create,创建了StringViewModel。
get方法简单总结一下就是:
https://segmentfault.com/a/1190000020515580
1.使用ViewModel Class的canonicalName作为ViewModel在ViewModelStore中的唯一标识。
2.通过唯一标识,先查询一下ViewModelStore中是否有该ViewModel对象,如果有则直接返回。
3.如果ViewModelStore中没有该ViewModel对象,则通过Factory工厂类反射创建出ViewModel对象,存入ViewModelStore中,并返回给调用者。
3.带参数的自定义ViewModel初始化流程
前面的代码流程为不带参数的自定义ViewModel初始化流程,现在来看看带参数的自定义ViewModel初始化流程。
初始化的时候,要多加一个参数:StringViewModelWithArg.Factory,并且需要重写getDefaultViewModelCreationExtras,设置CreationExtras 。
// MainActivity.kt
ViewModelProvider(this).get(StringViewModel::class.java)
ViewModelProvider(this,StringViewModelWithArg.Factory).get(StringViewModelWithArg::class.java)
override fun getDefaultViewModelCreationExtras(): CreationExtras {
super.getDefaultViewModelCreationExtras()
return MutableCreationExtras().apply {
set(ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY, application)
set(stringExtraKey, "EEE")
set(userExtraKey, "FFF")
}
}
//com.example.viewmodel.StringViewModelWithArg
// Define ViewModel factory in a companion object
companion object {
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
extras: CreationExtras
): T {
var string = "string"
try {
string = checkNotNull(extras.get(stringExtraKey))
}catch (e:Exception){
e.printStackTrace()
}
return StringViewModelWithArg(string) as T
}
}
}
会走到这里,传入一个自定义的factory,而前面不带参数StringViewModel的初始化使用的是defaultFactory(owner),也就是ComponentActivity中初始化的factory。
// androidx.lifecycle.ViewModelProvider
public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
owner.viewModelStore,
factory,
defaultCreationExtras(owner)
)
然后get(StringViewModelWithArg::class.java)
的时候会调用具体工厂的create 去创建具体产品 StringViewModelWithArg
。其余里流程一致。
记住工厂模式的4个点:
抽象工厂,具体工厂,抽象产品,具体产品,对应着框架去套,能更好地理解ViewModel初始化流程。
4.初始化简单总结
初始化调用流程简单总结:
调用ViewModelProvider构造函数
->创建ViewModelStore,用来存储ViewModel
->确定具体工厂(传入factory或使用defaultFactory(owner))
->通过ViewModelProvider.get(modelClass)调用具体工厂的create
->生产出具体产品,如StringViewModel,StringViewModelWithArg,
存入ViewModelStore。
一图胜千言,大致可以参考这张图:
而使用activity扩展库中的viewModels去初始化, 是扩展函数实现了 ViewModelProvider(this).get(StringViewModel::class.java)
的功能。
// MainActivity
private val stringViewModel: StringViewModel by viewModels()
private val stringViewModel by lazy {
ViewModelProvider(this).get(StringViewModel::class.java)
}
// ActivityViewModelLazyKt
@MainThread
public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
noinline extrasProducer: (() -> CreationExtras)? = null,
noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory
}
return ViewModelLazy(
VM::class,
{ viewModelStore },
factoryPromise,
{ extrasProducer?.invoke() ?: this.defaultViewModelCreationExtras }
)
}
通过by,将初始化StringViewModel的任务,委托给了viewModels()。
//ViewModelLazy
ViewModelProvider(
store,
factory,
extrasProducer()
).get(viewModelClass.java).also {
cached = it
}
// androidx.lifecycle.ViewModelProvider
构造函数
public constructor(
owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
调用get
get(StringViewModel::class.java)
参考链接:
Android源码解析-ViewModel
一文搞懂Android JetPack组件原理之Lifecycle、LiveData、ViewModel与源码分析技巧
ViewModel的创建