JetPack ViewModel (源码分析) 面试

ViewModel不仅是Activity和Fragment数据集中管理和通信类,而且促进了MVVMMVI架构规范。这篇文章希望帮助读者深入理解ViewModel,面试中对答如流。

一、ViewModel概念

ViewModel 是一个负责准备和管理ActivityFragment数据的类。它还处理 Activity / Fragment 与应用程序其余部分的通信(例如调用业务逻辑类)。

1、数据管理和通信

在早期的MVC模式开发场景中,大量的数据和业务逻辑处理被放置到了View层(Activity或者Fragment),Activity和Fragment之间的通信基本都是接口方式进行通信,还记得毕业那会儿老写三四千行的Activity,更甚至于13000行的代码,数据模型和业务逻辑处理被放到了Activity中,耦合度可谓三秒胶,更没有丁点阅读性,谁能读懂算我输。而ViewModel不仅可以将数据集中管理且可处理业务逻辑,极大的降低了MVC数据业务同View的耦合度,当然ViewModel也可以作为(Activity / Fragment)之间的通信桥梁。

ViewModel进行数据的集中管理,避免View层出现庞杂的数据以及数据处理逻辑代码,在LiveData、RxJava、Flow等观察者框架的基础上进行数据状态和UI的绑定,到达数据、逻辑处理和UI层面上真正分离。

Activity和Fragment相同上下文的所有页面可以通过共同ViewModel实例进行通信,告别接口或者依赖三方框架(EventBus等),减小代码的复杂度和方框架的入侵性。

2、生命周期感知

当年也是经受不住MVC折磨,在同事带领下也用上了MVP,但是,由于PresenterView层引用的持有内存泄露也没少处理,对于(屏幕旋转等场景的页面重置)数据状态的恢复也大费周折。最要命的是MVP并没有官方标准模板或者标准库的支持,市面上的模板可谓是千变万化,众说纷纭,人鬼神魔。各种复杂的接口实现让大多数新手退避三舍,同时也让初学者走了不少弯路。

ViewModel不需要开发者大量的接口实现以及手动释放资源,官方提供了其抽象类ViewModel,开发者直接继承就可以使用了,框架自身内部已经做好了安全性内存释放,避免了内存泄漏。方便了开发者统一的模板架构,也让移动端的MVVM架构更加成熟,同时优化了整个移动端开发的架构生态环境,让初学者有明确的架构框架层次认识,少走弯路。

3、数据持久性

下图是官方关于ViewModel生命周期(初始化到注销周期)和Activity生命周期的比对图。可以清晰看到有且只有Activity最终finished才会注销ViewModel,否则viewModel对象一直存在,也就保证了ViewModel中数据不会因为页面重建(屏幕旋转等)而丢失数据,一定程度上保证了页面UI的数据持久性,相信对于系统应用开发者带来了极大的便利性,尤其是适配横竖屏、分屏、浮窗、平板、电脑模式等相关的应用。 以上是对ViewModel在概念上进行相关的解释和大概的认识,便于接下来ViewModel使用做好基础铺垫。当然对于源码的分析也会奉上,不用着急。

二、ViewModel使用

ViewModel的使用极其简单,只需要开发者新建类CoustomViewModel 继承官方提供的抽象类ViewModel就可以了。以下是官方文档案例,使用Java编写的,但不影响举例说明。

kotlin 复制代码
 public class  LoginModel constructor extends ViewModel {
     public final LiveData<User> userLiveData = new LiveData<>();

     public UserModel() {
         // trigger user load.
     }

     void doAction() {
         // depending on the action, do necessary business logic calls and update the
         // userLiveData.
     }
 }

使用如下:

kotlin 复制代码
 public class UserActivity extends Activity {
     final LoginModel loginViewModel = ViewModelProviders.of(this).get(LoginModel.class);
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.user_activity_layout);

         loginViewModel.userLiveData.observer(this, new Observer() {
            @Override
             public void onChanged(@Nullable User data) {
                 // update ui.
             }
         });
         findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                  loginViewModel.doAction();
             }
         });
     }
 }

1、Activity 中使用

当然很多开发者也遇到其他创建方式。而点击查看都不难发现最终都是通过ViewModelProvider进行创建viewModel实例的。by lazy方式通过by将ViewModel初始化委托给lazy进行延迟初始化。其所有的创建方式,最终都是通过 ViewModelProvider.create 进行创建viewModel实例。

kotlin 复制代码
val loginViewModel = ViewModelProvider(this)[LoginViewModel::class.java]
val loginViewModel by lazy {
    ViewModelProvider(this)[LoginViewModel::class.java]
}
val loginViewModel by viewModels<LoginViewModel>()

当然也可以自己定义工厂,通过ViewModelProvider constructor(owner:ViewModelStoreOwner,factory:Factory)进行创建ViewModel,例如开发者需要给ViewModel注入其他参数就可以自定义工厂通过ViewModelProvider两个参数的构造方法进行创建。可能开发者需要给ViewModel注入一个Tool对象。

kotlin 复制代码
//调用方式
val viewModel: LoginViewModel by customViewModels()


@Suppress("UNCHECKED_CAST")
class CustomViewModelFactory(private val tool: Tool) :
    ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(LoginViewModel::class.java)) {
            return LoginViewModel(loginRepository = LoginRepository(tool)) as T
        } else if (modelClass.isAssignableFrom(HomeViewModel::class.java)) {
            return HomeViewModel(homeRepository = HomeRepository(tool)) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

@MainThread
inline fun <reified VM : ViewModel> Context.customViewModels(
    noinline extrasProducer: (() -> CreationExtras)? = null,
    noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
): Lazy<VM> {
    val context = this as ComponentActivity
    val factoryPromise = factoryProducer ?: {
        CustomViewModelFactory(WeakReference(context))
    }

    return ViewModelLazy(
        VM::class,
        { viewModelStore },
        factoryPromise,
        { extrasProducer?.invoke() ?: context.defaultViewModelCreationExtras }
    )
}

2、Fragment 中使用

在Fragment中如果想和所在Activity共用同一个ViewModel,就使用如下activityViewModels方式进行实例化。如果单独自身使用,而不需要同所在Activity使用同一个ViewModel就使用viewModels进行实例化。

kotlin 复制代码
记得导入相关的依赖
//androidx.fragment:fragment-ktx
//androidx.activity:activity-ktx
class BlankFragment : Fragment() {
    val activityViewModel : LoginViewModel by activityViewModels()
    val viewModel : LoginViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }
}


//activityViewModels扩展方法如下:通过requireActivity().viewModelStore获取
//的ViewModelStore所以是所在CompnentActivity内部的_viewModelStore。
@MainThread
public inline fun <reified VM : ViewModel> Fragment.activityViewModels(
    noinline extrasProducer: (() -> CreationExtras)? = null,
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> = createViewModelLazy(
    VM::class, { requireActivity().viewModelStore },
    { extrasProducer?.invoke() ?: requireActivity().defaultViewModelCreationExtras },
    factoryProducer ?: { requireActivity().defaultViewModelProviderFactory }

)

而在Compose应用中,可能更多看到的是如下实例化,如下使用了Hilt依赖注入框架,就可以通过hiltViewModel进行实例化。

kotlin 复制代码
//1、常规初始化方法
viewModel1: LoginViewModel = viewModel()
//2、如果使用了依赖注入框架
viewModel: LoginViewModel = hiltViewModel()

@Composable
fun LoginScreen(
    navigateToMain: (String) -> Unit = {},
    navigateToRegister: (String) -> Unit = {},
    viewModel: LoginViewModel = hiltViewModel(),//如果使用了Hilt依赖注入框架
    viewModel1: LoginViewModel = viewModel()//常规实例化
) {
    val rememberColorState = remember {
        mutableStateOf(Color(0xFF2FC97C))
    }
}

目前大家可以很快速的根据官方文档进行使用,但是面试过程大概率的会问道ViewModel的具体实现原理和相关源码,只有深入源码有所理解整体实现,才会在面试中游刃有余,所以接下来进行详细的源码分析,以便于更好的理解ViewModel的实现以及更好的面试。

三、ViewModel源码分析

源码的学习过程是枯燥无味的,对于文章学习者最好可以自己跟随文章进行点击源码查看,可能会更加清晰明了。我是将ViewModel相关的核心类和接口各个击破,然后根据整个生命周期进行串联起来,希望对大家有所帮助。

源码分析版本

viewModel: androidx.lifecycle:lifecycle-viewmodel-android:2.8.0@aar、 Activity: androidx.activity:activity:1.9.0@aar

1、ViewModelStoreOwner

一个接口,用来获取一个ViewModelStore对象。不清楚的开发者可以看编译为Java之后的代码。

kotlin 复制代码
/**
 * A scope that owns [ViewModelStore].
 *
 * A responsibility of an implementation of this interface is to retain owned ViewModelStore
 * during the configuration changes and call [ViewModelStore.clear], when this scope is
 * going to be destroyed.
 *
 * @see ViewTreeViewModelStoreOwner
 */
public interface ViewModelStoreOwner {

    /**
     * The owned [ViewModelStore]
     */
    public val viewModelStore: ViewModelStore
}实力

//Decompile Java 编译为Java之后的代码

public interface ViewModelStoreOwner {
   @NotNull
   ViewModelStore getViewModelStore();
}

2、ViewModelStore

存储多个ViewModel在map中,在配置改变时ViewModelStore将会封装到NonConfigurationInstances,然后储存到应用数据结构ActityClientRecord中,最终进入到主线程集合ArrayMap。重建之后,再次拿到储存的ActivityClientRecord,所以还是上次的ViewModelStore。这才是ViewModel不受配置改变影响的本质。当然后面会用源码分析。

kotlin 复制代码
public open class ViewModelStore {
    //储存ViewModel的集合容器
    private val map = mutableMapOf<String, ViewModel>()

    /**
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public fun put(key: String, viewModel: ViewModel) {
        val oldViewModel = map.put(key, viewModel)
        oldViewModel?.clear()
    }

    /**
     * Returns the `ViewModel` mapped to the given `key` or null if none exists.
     */
    /**
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public operator fun get(key: String): ViewModel? {
        return map[key]
    }

    /**
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public fun keys(): Set<String> {
        return HashSet(map.keys)
    }

    /**
     * Clears internal storage and notifies `ViewModel`s that they are no longer used.
     */
    public fun clear() {
        for (vm in map.values) {
            vm.clear()
        }
        map.clear()
    }
}

3、ViewModelProvider

通过ViewModelProvider(this).get(CustomViewModel::class.java)传入了this (这里的this即实现了ViewModelStoreOwnerComponentActivity)。ViewModelProvider 负责将ViewModelProvider.Factory 创建的 ViewModel 实例储存到ViewModelStoreOwner(即ComponentActivity)的ViewModelStore中。

方便了当ComponentActivity发生配置改变时将其ViewModelStore最终分装到ActityClientRecord,储存在主线程的ArrayMap中,页面重建之后方便恢复之前储存的ViewModel。接下来咱们分析是如何储存到ActivityClientRecord中的,又是如何恢复ViewModelStore,保证ViewModel不受配置影响的。

kotlin 复制代码
public actual open class ViewModelProvider private constructor(
    private val impl: ViewModelProviderImpl,
) {


    public constructor(
        owner: ViewModelStoreOwner,
    ) : this(
        store = owner.viewModelStore,
        factory = ViewModelProviders.getDefaultFactory(owner),
        defaultCreationExtras = ViewModelProviders.getDefaultCreationExtras(owner)
    )

    @JvmOverloads
    public constructor(
        store: ViewModelStore,
        factory: Factory,
        defaultCreationExtras: CreationExtras = CreationExtras.Empty,
    ) : this(ViewModelProviderImpl(store, factory, defaultCreationExtras))

    @MainThread
    public actual operator fun <T : ViewModel> get(modelClass: KClass<T>): T =
        impl.getViewModel(modelClass)

    @MainThread
    public open operator fun <T : ViewModel> get(modelClass: Class<T>): T =
        get(modelClass.kotlin)

    @MainThread
    public actual operator fun <T : ViewModel> get(key: String, modelClass: KClass<T>): T =
        impl.getViewModel(modelClass, key)

    @MainThread
    public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T =
        impl.getViewModel(modelClass.kotlin, key)
}


internal class ViewModelProviderImpl(
    private val store: ViewModelStore,
    private val factory: ViewModelProvider.Factory,
    private val extras: CreationExtras,
) {

    constructor(
        owner: ViewModelStoreOwner,
        factory: ViewModelProvider.Factory,
        extras: CreationExtras,
    ) : this(owner.viewModelStore, factory, extras)

    @Suppress("UNCHECKED_CAST")
    internal fun <T : ViewModel> getViewModel(
        modelClass: KClass<T>,
        key: String = ViewModelProviders.getDefaultKey(modelClass),
    ): T {
        //如果ComponentActivity中的ViewModelStore中存在当前modelClass的实例
        //那就直接返回。发生场景应配置更改重建页面时,拿到的还是之前的viewModel。
        val viewModel = store[key]
        if (modelClass.isInstance(viewModel)) {
            if (factory is ViewModelProvider.OnRequeryFactory) {
                factory.onRequery(viewModel!!)
            }
            return viewModel as T
        } else {
            @Suppress("ControlFlowWithEmptyBody") if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        val extras = MutableCreationExtras(extras)
        extras[ViewModelProviders.ViewModelKey] = 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.
        //否则通过factory.create进行首次创建viewModel并最后通过store.put()
        //将viewModel储存到ComponentActivity的全局变量viewModelStore中。
        return try {
            factory.create(modelClass, extras)
        } catch (e: Error) { // There is no `AbstractMethodError` on KMP, using common ancestor.
            factory.create(modelClass, CreationExtras.Empty)
        }.also { newViewModel -> store.put(key, newViewModel) }
    }
}

4、ViewModelProviders.Factory

ViewModelProviders.Factory作为生产ViewModel的工厂,它是ViewModelProvider内部接口,其实现子类有 AndroidViewModelFactory 、 NewInstanceFactory 、 DefaultViewModelProviderFactory、HiltViewModelFactory...等。在首次通过ViewModelProvider(this).get(ViewModel::class.java)时,在如下源码ViewModelProvider构造方法中通过ViewModelProviders.getDefaultFactory(owner)去获取创建当前ViewModel的工厂,因为ComponentActivity实现了 HasDefaultViewModelProviderFactory,类型如果owner 是 HasDefaultViewModelProviderFactory类型,就拿到ComponentActivity中的工厂,否则通过DefaultViewModelProviderFactory实例反射创建ViewModel。

kotlin 复制代码
public actual open class ViewModelProvider private constructor{

    //1、首次初始化viewModle时候val viewModle = ViewModelProvider(this).get(LoginViewModel::class.java)
    public constructor(
        owner: ViewModelStoreOwner,
    ) : this(
        store = owner.viewModelStore,
        factory = ViewModelProviders.getDefaultFactory(owner),
        defaultCreationExtras = ViewModelProviders.getDefaultCreationExtras(owner)
    )
    //2、获取当前合适的工厂
    internal fun getDefaultFactory(owner: ViewModelStoreOwner): ViewModelProvider.Factory =
        if (owner is HasDefaultViewModelProviderFactory) {
            owner.defaultViewModelProviderFactory
        } else {
            DefaultViewModelProviderFactory
        }


    public actual 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 =
            ViewModelProviders.unsupportedCreateViewModel()

        /**
         * Creates a new instance of the given `Class`.
         *
         * @param modelClass a `Class` whose instance is requested
         * @param extras an additional information for this creation request
         * @return a newly created ViewModel
         */
        public fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T =
            create(modelClass)

        public actual fun <T : ViewModel> create(
            modelClass: KClass<T>,
            extras: CreationExtras,
        ): T = create(modelClass.java, extras)

        public companion object {
            /**
             * Creates an [InitializerViewModelFactory] using the given initializers.
             *
             * @param initializers the class initializer pairs used for the factory to create
             * simple view models
             *
             * @see [InitializerViewModelFactory]
             */
            @JvmStatic
            public fun from(vararg initializers: ViewModelInitializer<*>): Factory =
                ViewModelProviders.createInitializerFactory(*initializers)
        }
    }

DefaultViewModelProviderFactory源码如下、通过JvmViewModelProvider.createViewModel反射创建ViewModel实例。

kotlin 复制代码
internal actual object DefaultViewModelProviderFactory : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: KClass<T>, extras: CreationExtras): T =
        JvmViewModelProviders.createViewModel(modelClass.java)
}

internal object JvmViewModelProviders{
  fun<T:ViewModel> createViewModel(modelClass:Class<T>):T = try {
  try{
     modelClass.getDeclaredConstructor().newInstance()
     } catch
     ///省略其他.....
  }
}

NewInstanceFactory源码如下

kotlin 复制代码
public open class NewInstanceFactory
/**
 * Construct a new [NewInstanceFactory] instance.
 *
 * Use [NewInstanceFactory.instance] to get a default instance of [NewInstanceFactory].
 */
@Suppress("SingletonConstructor")
constructor() : Factory {

    public override fun <T : ViewModel> create(modelClass: Class<T>): T =
        JvmViewModelProviders.createViewModel(modelClass)

    public override fun <T : ViewModel> create(
        modelClass: Class<T>,
        extras: CreationExtras,
    ): T = create(modelClass)

    public override fun <T : ViewModel> create(
        modelClass: KClass<T>,
        extras: CreationExtras,
    ): T = create(modelClass.java, extras)

    public companion object {
        private var _instance: NewInstanceFactory? = null

        /**
         * Retrieve a singleton instance of NewInstanceFactory.
         *
         * @return A valid [NewInstanceFactory]
         */
        @JvmStatic
        public val instance: NewInstanceFactory
            @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
            get() {
                if (_instance == null) {
                    _instance = NewInstanceFactory()
                }
                return _instance!!
            }

        /**
         * A [CreationExtras.Key] used to retrieve the key associated with a requested [ViewModel].
         *
         * The [ViewModelProvider] automatically includes the key in the [CreationExtras] passed to
         * [ViewModelProvider.Factory]. This applies to keys generated by either of these usage
         * patterns:
         * - `ViewModelProvider.get(key, MyViewModel::class)`: provided `key` is used.
         * - `ViewModelProvider.get(MyViewModel::class)`: generates a `key` from given `class`.
         *
         * @see ViewModelProvider.VIEW_MODEL_KEY
         */
        @JvmField
        public val VIEW_MODEL_KEY: Key<String> = ViewModelProviders.ViewModelKey
    }
}

AndroidViewModelFactory源码如下:

kotlin 复制代码
    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() {

        /**
         * Constructs this factory.
         *
         * @param application an application to pass in [AndroidViewModel]
         */
        @Suppress("SingletonConstructor")
        public constructor(application: Application) : this(application, unused = 0)

        @Suppress("DocumentExceptions")
        override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
            return if (application != null) {
                create(modelClass)
            } else {
                val application = extras[APPLICATION_KEY]
                if (application != null) {
                    create(modelClass, application)
                } else {
                    // For AndroidViewModels, CreationExtras must have an application set
                    if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
                        throw IllegalArgumentException(
                            "CreationExtras must have an application by `APPLICATION_KEY`"
                        )
                    }
                    super.create(modelClass)
                }
            }
        }

        @Suppress("DocumentExceptions")
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return if (application == null) {
                throw UnsupportedOperationException(
                    "AndroidViewModelFactory constructed " +
                        "with empty constructor works only with " +
                        "create(modelClass: Class<T>, extras: CreationExtras)."
                )
            } else {
                create(modelClass, application)
            }
        }

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

        public companion object {
            private var _instance: AndroidViewModelFactory? = null

            /**
             * Retrieve a singleton instance of AndroidViewModelFactory.
             *
             * @param application an application to pass in [AndroidViewModel]
             * @return A valid [AndroidViewModelFactory]
             */
            @JvmStatic
            public fun getInstance(application: Application): AndroidViewModelFactory {
                if (_instance == null) {
                    _instance = AndroidViewModelFactory(application)
                }
                return _instance!!
            }

            /**
             * A [CreationExtras.Key] to query an application in which ViewModel is being created.
             */
            @JvmField
            public val APPLICATION_KEY: Key<Application> = object : Key<Application> {}
        }
    }

HasDefaultViewModelProviderFactory接口源码

kotlin 复制代码
    public interface HasDefaultViewModelProviderFactory {
        /**
         * Returns the default [ViewModelProvider.Factory] that should be
         * used when no custom `Factory` is provided to the
         * [ViewModelProvider] constructors.
         */
        public val defaultViewModelProviderFactory: ViewModelProvider.Factory

        /**
         * Returns the default [CreationExtras] that should be passed into
         * [ViewModelProvider.Factory.create] when no overriding
         * [CreationExtras] were passed to the [ViewModelProvider] constructors.
         */
        public val defaultViewModelCreationExtras: CreationExtras
            get() = CreationExtras.Empty
    }

5、ComponentActivity

实现了ViewModelStoreOwner,持有viewModelStore,在首次通过ViewModelProvider(this).get(CoustomViewModel::class.java) 创建时,因为ComponentActivity 实现了 ViewModelStoreOwner,所以在创建完成 viewModel 之后通过传入的 this 进行给 ComponentActivity 局部的 _viewModelStore 赋值。在下面代码中也可以看到当首次获取 viewModelStore 时,通过调用ensureViewModelStore 来判断是否当前 ComponentActivitylastNonConfigurationInstance 是否为 null,如果不为空 _viewModelStore = nc.viewModelStore ,否则新建一个_viewModelStore = ViewModelStore()

kotlin 复制代码
open class ComponentActivity() : androidx.core.app.ComponentActivity(),
    ...,
    LifecycleOwner,
    ViewModelStoreOwner,
    ... {
        //储存viewModelStore
        private var _viewModelStore: ViewModelStore? = null
        override val lifecycle: Lifecycle
            get() = super.lifecycle

        override val viewModelStore: ViewModelStore
            /**
             * Returns the [ViewModelStore] associated with this activity
             *
             * Overriding this method is no longer supported and this method will be made
             * `final` in a future version of ComponentActivity.
             *
             * @return a [ViewModelStore]
             * @throws IllegalStateException if called before the Activity is attached to the
             * Application instance i.e., before onCreate()
             */
            get() {
                checkNotNull(application) {
                    ("Your activity is not yet attached to the " +
                        "Application instance. You can't request ViewModel before onCreate call.")
                }
                ensureViewModelStore()
                return _viewModelStore!!
            }

        private fun ensureViewModelStore() {
            //如果_viewModelStore为null
            if (_viewModelStore == null) {
                //判断lastNonConfigurationInstance是否为null,否则拿到上次储存在里面的viewModelStore
                val nc = lastNonConfigurationInstance as NonConfigurationInstances?
                if (nc != null) {
                    // Restore the ViewModelStore from NonConfigurationInstances
                    _viewModelStore = nc.viewModelStore
                }
                //首次创建页面,创建ViewModelStore
                if (_viewModelStore == null) {
                    _viewModelStore = ViewModelStore()
                }
            }
        }

    }

6、Activity

Activity 孩子类 ComponentActivity 重写了 onRetainNonConfigurationInstance 方法,方法中将ComponentActivity中的_viewModel赋值给了NonConfigurationInstances,返回给了这里。当屏幕旋转之后再 ActivityThread 中执行 performDestroyActivity时 ,通过 r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances(); 拿到Activity 这里的 NonConfigurationInstances 赋值给 r(ActivityClientRecord),最终实现了ComponentActivity 中的 _viewModelStore 被保存到了 ActivityClientRecord中。

当配置发生改变,ActivityThread 执行到 performDestoryActivity之后,通过r.activity.retainNonConfigurationInstances() 调用了Activity 内部的 retainNonConfigurationInstances 方法,而在方法里面Object activity = onRetainNonConfigurationInstance() 被 ComponentActivity 所覆写的 onRetainNonConfigurationInstance 且返回了ComponentActivity 中分装了 _viewModel的NonConfigurationInstances。最终分装为Activity中的 NonConfigurationInstances 赋值给了r.lastNonConfigurationInstances。需要了解其ComponentActivity 和 Activity中的NonConfigurationInstances不是同一个类。

kotlin 复制代码
//ComponentActivity
open class ComponentActivity():Activity{
    //用于储存viewModelStore的配置容器类
    internal class NonConfigurationInstances {
        var custom: Any? = null
        var viewModelStore: ViewModelStore? = null
    }

    @Suppress("deprecation")
    final override fun onRetainNonConfigurationInstance(): Any? {
        // Maintain backward compatibility.
        val custom = onRetainCustomNonConfigurationInstance()
        var viewModelStore = _viewModelStore
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last NonConfigurationInstance
            val nc = lastNonConfigurationInstance as NonConfigurationInstances?
            if (nc != null) {
                viewModelStore = nc.viewModelStore
            }
        }
        if (viewModelStore == null && custom == null) {
            return null
        }
        val nci = NonConfigurationInstances()
        nci.custom = custom
        nci.viewModelStore = viewModelStore
        return nci
    }
}

Activity源码:

kotlin 复制代码
public class Activity extends...{
    //孩子ComponentActivity重写了此方法,并将ComponentActivity
    //中的_viewModel赋值给了NonConfigurationInstances,返回给了这里。
    public Object onRetainNonConfigurationInstance() {
        return null;
    }
    //需要区分CompnentActivity中的NonConfigurationInstances不是同一个类。
    static final class NonConfigurationInstances {
        Object activity;
        HashMap<String, Object> children;
        FragmentManagerNonConfig fragments;
        ArrayMap<String, LoaderManager> loaders;
        VoiceInteractor voiceInteractor;
    }

    //当屏幕旋转之后再ActivityThread 中执行 performDestroyActivity时
    //通过 r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
    //拿到Activity这里的NonConfigurationInstances赋值给r(ActivityClientRecord)
    NonConfigurationInstances retainNonConfigurationInstances() {
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

        // We're already stopped but we've been asked to retain.
        // Our fragments are taken care of but we need to mark the loaders for retention.
        // In order to do this correctly we need to restart the loaders first before
        // handing them off to the next activity.
        mFragments.doLoaderStart();
        mFragments.doLoaderStop(true);
        ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();

        if (activity == null && children == null && fragments == null && loaders == null
                && mVoiceInteractor == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;
        nci.children = children;
        nci.fragments = fragments;
        nci.loaders = loaders;
        if (mVoiceInteractor != null) {
            mVoiceInteractor.retainInstance();
            nci.voiceInteractor = mVoiceInteractor;
        }
        return nci;
    }
    //第十步
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    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,
            IBinder shareableActivityToken) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);
        mActivityInfo = info;

        //.....省略其他代码......  
        //第11步
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        //.....省略其他代码...... 
    }

}

7、ActivityThread

当发生配置变化(屏幕旋转)之后,ActivityManagerService 接收到重新创建的消息,会通知应用的主线程 ActivityThread 内部的 handleRelaunchActivity,接着依次调用了 handleRelaunchActivityInner 、handleDestroyActivity 、performDestroyActivity、直到 第六步 performDestroyActivity 在其内部通过 r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances(); 进行给当前 ActivityClientRecord 的 lastNonConfigurationInstances 赋值为当前 Activity(ComponentActivity)NonConfigurationInstances,实现了 ComponentActivity_viewModel 的储存。

接着执行到第七步可以看到传入了上面 handleDestroyActivity 同样的ActivityClientRecord ,最终调到第九步 activity.attach ,我们再看看上面Activity 源码的第十步attach里面,可以发现 最终将 ActivityClientRecord 的lastNonConfigurationInstances 赋值给了 Activity 的mLastNonConfigurationInstances。这样就完成了页面重建之后拿到的NonConfigurationInstances 还是销毁时候储存在 ActivityClientRecord 中的lastNonConfigurationInstances,这正是页面配置变化之后为啥能拿到相同viewModel的本质。

kotlin 复制代码
class ActivityThread{

    @UnsupportedAppUsage
    final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();

    //第四步
     @Override
    public void handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges,
            boolean getNonConfigInstance, String reason) {
        //第五步 
        performDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason);

   }
   //第六步 
    void performDestroyActivity(ActivityClientRecord r, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
        //.....省略其他代码......
        if (getNonConfigInstance) {
            try {
                //屏幕发生旋转,页面重键,会走到这里,通过r.activity.retainNonConfigurationInstance()调用了Activity中的
                //retainNonConfigurationInstances最终实现了ComponentActivity内部的_viewModleStore被保存到了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);
                }
            }
        }
      //.....省略其他代码......   
    }
 //.....省略其他代码......
 
 
   //第一步
   @Override
    public void handleRelaunchActivity(ActivityClientRecord tmp,
            PendingTransactionActions pendingActions) {
       //.....省略其他代码...... 
        handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
                pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
    }

   //第二步
   private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
            PendingTransactionActions pendingActions, boolean startsNotResumed,
            Configuration overrideConfig, String reason) {
        // Preserve last used intent, it may be set from Activity#setIntent().
        final Intent customIntent = r.activity.mIntent;
        // Need to ensure state is saved.
        if (!r.paused) {
            performPauseActivity(r, false, reason, null /* pendingActions */);
        }
        if (!r.stopped) {
            callActivityOnStop(r, true /* saveState */, reason);
        }
        //第三步
        handleDestroyActivity(r, false, configChanges, true, reason);
         //.....省略其他代码...... 
        //第七步
        handleLaunchActivity(r, pendingActions, mLastReportedDeviceId, customIntent);
    }

    
    @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, int deviceId, Intent customIntent) {
      
         //.....省略其他代码...... 
        //第八步
        final Activity a = performLaunchActivity(r, customIntent);

        //.....省略其他代码...... 


        return a;
    }

   //第九步 
   private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       //这里将ActivityClientRecord存储在mActivities,只要应用进程没有销毁下次拿到的还是上次储存的ActivityClientRecord。
        synchronized (mResourcesManager) {
            mActivities.put(r.token, r);
        }
      
      //.....省略其他代码......  
       //最终调用了Activity的attach方法。
       activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
                        r.assistToken, r.shareableActivityToken);
      //.....省略其他代码......   
  }
}

8、ActivityClientRecord

ActivityClientRecord 的作用是跟踪和管理每个活动(Activity)的状态和信息。它记录了Activity的各种信息,包括活动实例、Intent、状态标志等。 这个类通常在应用程序的生命周期中被创建和使用。它在活动被创建时被实例化,并在Activity生命周期结束时被清除。清除的时机通常是当活动被销毁(比如用户退出当前Activity或系统回收资源时) ActivityThread 将相应的 ActivityClientRecord 从其内部管理的列表 mActivities 中移除,释放相应的资源。

而配置改变屏幕发生旋转系统将会执行上述ActivityThread 内部的 handleRelaunchActivity 流程,将保存在r(ActivityClientRecord)中的临时mLastNonConfigurationInstances 最终设置给恢复之后的Activity,所以最终保证了 NonConfigurationInstances(ViewModelStore(ViewModel)) 在发生配置改变前后保证前后一致。

kotlin 复制代码
public final class ActivityThread extends ClientTransactionHandler
        implements ActivityThreadInternal {
        
    /**
     * Maps from activity token to local record of running activities in this process.
     *
     * This variable is readable if the code is running in activity thread or holding {@link
     * #mResourcesManager}. It's only writable if the code is running in activity thread and holding
    * {@link #mResourcesManager}.
     */
    @UnsupportedAppUsage
    final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
    
    
       //ActivityClientRecord是ActivityThread的内部类。
    public static final class ActivityClientRecord {
        @UnsupportedAppUsage
        public IBinder token;
        public IBinder assistToken;
        // A reusable token for other purposes, e.g. content capture, translation. It shouldn't be
        // used without security checks
        public IBinder shareableActivityToken;
        // The token of the TaskFragment that embedded this activity.
        @Nullable public IBinder mTaskFragmentToken;
        int ident;
        @UnsupportedAppUsage
        Intent intent;
        Bundle state;
        @UnsupportedAppUsage
        Activity activity;
         //.....省略其他代码......  
        Activity.NonConfigurationInstances lastNonConfigurationInstances;//关键地方。储存NonConfigurationInstances
         //.....省略其他代码......  
         
        
    }     
}

根据上述对相关接口和类源码的逐一分析,也大概明白了viewModel对数据的持有不受配置改变重建而造成丢失。接下来,从viewModel的创建到配置改变进行储存以及重建之后任然拿到上次的viewModel做一个整体的串联,有助于整体对源码的掌握。

四、源码串联

1、创建ViewModelStore并设置给ComponentActivity

通过ViewModelProvider(this).get(CustomViewModel::class.java) 进行获取CustomViewModel实例。ViewModelProvider传进来的this是当前Activity(ComponentActivity),ComponentActivity自身实现了ViewModelStoreOwner,ViewModelStoreOwner 为 CompnentActivity 对外提供了 getViewModelStore(): ViewModelStore 用于外部获取创建ViewModelStore。ViewModelStore(储存ViewModel的容器)在CompnentActivity 的getViewModelStore()方法中调用了ensureViewModelStore方法,其内部通过lastNonConfigurationInstance先获取是否配置发生变化之后恢复拿到nc.viewModelStore,否则新建ViewModelStore() 并保存在了 ComponentActivity全局变量_viewModelStore中,最终返回给ViewProvider内部用于将创建好的ViewModel储存到ComponentActivity中的_viewModelStore中。

2、创建ViewModel设置给_viewModelStore

当ViewModelProvider内部执行get(CustomViewModel::class.java)时候,首先会通过传进来的ComponentActivity获取(ComponentActivity)的ViewModelStore,并通过key从(ComponentActivity)的ViewModelStore 获取是否存在CustomViewModel实例,如果存在返回上一次储存的ViewModel否则通过Factory.create创建CustomViewModel的新实例,并通过传进来的this 拿到 ViewModelStore(store.put(key,newViewModel))将新的ViewModel实例设置到ComponentActivity的ViewModelStore中。

3、配置改变NonConfigurationInstances到ActivityClientRecord

当Activity的配置发生改变(屏幕旋转),会执行到ActivityThread 的 handleRelaunchActivity流程->handleRelaunchActivityInner -> handleDestroyActivity -> performDestroyActivity ,在performDestoryActivity方法内部,通过r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances(); 调用当前Activity的retainNonConfigurationInstances 方法进行 其方法内部调用onRetainNonConfigurationInstance方法,应为ComponentActivity继承了Activity,所以最终调用了ComponentActivity的onRetainNonConfigurationInstance方法,返回给Activity 分装好的NonConfigurationInstances(需要注意这里的NonConfigurationInstances是ComponentActivity内部类和Activity内部的NonConfigurationInstances是不同的)对象。并在Activity的retainNonConfigurationInstances方法中将其转换为Activity中的NonConfigurationInstances对象,返回给ActivityThread 的 r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();调用处最终将Activity中的含有ViewModelStore的NonConfigurationInstances储存到ActivityClientRecord中。

4、activity.attach将r中的NonConfigurationInstances设置到Activity

在上述执行handleRelaunchActivity流程->handleRelaunchActivityInner -> handleDestroyActivity ,在handleDestroyActivity(r,false,configChanges,true,reason)执行完成将NonConfigurationInstances储存到当前r(ActivityClientRecord)之后,接着执行了handleLaunchActivity(r, pendingActions, mLastReportedDeviceId, customIntent);在其内部接着调用了performLaunchActivity(r, customIntent);在其内部最终调用了activity.attach(..., r.lastNonConfigurationInstances,...);,需要明确在发生配置改变之后,在整个流程中前后先给r赋值lastNonConfigurationInstances然后在activity.attach中拿着上面的赋值结果再次赋值给Activity,所以保证了lastNonconfigurationInstance前后一致,即保证了配置改变前后ViewModelStore 前后一致,最终 ViewModel也拿到的是同一个。

5、Activity中的mLastNonConfigurationInstances保存数据

在Activity的attach方法可以看到,ActivityThread的performLaunchActivity方法中通过,activity.attach将ActivityClientRecord中的lastNonconfigurationInstance传递给了Activity中的mLastNonConfigurationInstances。

6、初始化ViewModel时最终调用Activity中mLastNonConfigurationInstances

回到上面第一步中当通过ViewModelProvider(this).get(CustomViewModel::class.java) 获取ViewModel时,在ComponentActivity中的getViewModelStore()方法中调用了ensureViewModelStore方法,而在ensureViewModelStore方法中通过getLastNonConfigurationInstance获取了Activity中储存mLastNonConfigurationInstances,并获取了mLastNonConfigurationInstances中储存的上一个viewModelStore从而拿到的是同一个ViewModel这里形成闭环。

五、ViewModel何时会销毁

在ComponentActivity中对当前活动进行了生命周期的监听,如下源码中可以看到,当页面被销毁时收到相关销毁通知,在其訂閱者内部可以看到 isChangingConfigurations 配置没有发生改变(屏幕未旋转),会执行viewModelStore.clear()将其内部map中的所有ViewModel都清除。

1、正常退出

用户点击导航栏返回键,finish了此页面,进入上级页面。或者点击了finish当前页面的相关按钮。

kotlin 复制代码
open class ComponentActivity(){
    lifecycle.addObserver(LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_DESTROY) {
            // Clear out the available context
            contextAwareHelper.clearAvailableContext()
            // And clear the ViewModelStore
            if (!isChangingConfigurations) {
                viewModelStore.clear()
            }
            reportFullyDrawnExecutor.activityDestroyed()
        }
    })
}

2、异常退出

页面未响应或者因为应用内存原因导致的异常退出,也并非配置改变,当执行到lifecycle.addObserver的监听者内部时,应为非配置改变,所以最终也会被clear掉。

3、应用进程被杀

当应用由于用户后台释放、内存泄漏、事件无响应....导致应用进程被杀,是无法恢复数据的,应用的整个内存都会被释放。

六、总结

ViewModel 不仅仅解决了因为页面配置改变而丢失数据的问题,而且承担了数据载体和业务处理等与界面无关的职责,降低了业务、数据和UI的耦合度。更大程度上促进了MVVM以及MVI架构的成熟和发展。开发者更应该了解背后的相关原理,不管在面试还是在使用都胸有成竹。

UML图上传的是SVG,上传之后不知道是否清晰,文章比较仓库,写的比较粗糙,有错误和问题可以评论区提出。

相关推荐
夜色呦2 小时前
深入 Laravel 的错误处理与异常处理机制
android·php·laravel
openEuler社区3 小时前
openEuler 社区 2024 年 5 月运作报告
开源·操作系统·openeuler
JoyceMill3 小时前
Android动画:提升用户体验的关键技术
android·ux
s_nshine3 小时前
将 build.gradle 配置从 Groovy 迁移到 Kotlin
android·开发语言·kotlin·gradle·groovy·build
Jerry_正弦3 小时前
Kotlin中object关键字的作用
android·开发语言·kotlin
buyue__3 小时前
Kotlin/Android中执行HTTP请求
android·开发语言·kotlin
腾讯数据架构师3 小时前
cube-studio 开源一站式云原生机器学习/深度学习/大模型训练推理平台介绍
机器学习·云原生·开源
JoyceMill3 小时前
Android 图像切换器:实现动态图像切换的关键技术与应用
android
敲代码的阳哥shen1616114 小时前
抖音矩阵系统源码开发实现功能路径-saas源码开发
矩阵·开源·视频
CSDN官方博客4 小时前
走进开源企业 | 湖南大学OpenHarmony技术实训活动在开鸿智谷顺利举办!
开源