ViewModel相关

郭霖公众号
原作者原文

前言

ViewModel不仅是Activity和Fragment的数据集中管理和通讯,也是促进了MVVM和MVI架构规范,此文为深入理解ViewModel

概念

ViewModel是复制准备和管理Activity和Fragment数据的类,他还处理Activity或Fragment与应用程序其他部分的通信,例如调用业务逻辑。

1 数据管理和通信

早期MVC的模式开发场景中,大量的数据和业务逻辑处理放到了View层 也就是Activity和Fragment里,Activity和Fragment直接通信都是用接口回调方式通信,导致代码超长,上万行,数据模型和业务逻辑处理被放到了Activity里,导致耦合度很高,没有可读性,Viewmodel的目的就是将数据集中管理,且可处理业务逻辑,极大的降低了,mvc数据业务 和view的耦合度,当然Viewmodel也可以作为Ac/Frag 之间通信的桥梁。

viewModel 进行数据的集中管理,避免View层吗出行庞杂的数据和数据处理逻辑代码,再LiveData Rxjava Flow等观察者框架的基础下进行数据状态和UI绑定,到达数据,逻辑处理和Ui层面上的真正分离。

Activity和Fragment相同的上下文的所有页面可以通过共同的Viewmodel实例进行通信,告别接口或者Bus类三方,减小代码复制和三放框架入侵。

2 生命周期感知

MVP的缺点:大量的接口,代码量大,Presenter对view持有,内存泄露,切换屏幕的数据恢复 等等,

viewModel的优点:不需要开发者大量的接口实现和手动是否,官方提供了抽象类ViewModel,直接继承使用,内部已经实现号了安全性的内存释放,避免内存泄露,方便统一开发者模版架构,让移动端的MVVM架构更成熟,同时优化了整个移动端开发架构生态环境,让初学者有明确的架构框架层次认识少走弯路。

3数据持久性

在这里插入图片描述
图是官方关于viewModel生命周期和Activity生命周期对比图,可以看出来,只有Activity
最终的finished才会注销ViewModel,否则Viewmodel对象一致存在也就抱着了 VM的数据不会因为页面重建(旋转等)而丢失数据,一定程度保证了页面UI的数据持久性,相信对系统应用开发带来了极大的便利性,尤其适配横竖屏分配 浮窗 平板电脑等模式的相关应用
ps:Ac的onDestory 里会判断是旋转还是真的要销毁,会打Flag,onCleared的时候判断区分是否要清除。

使用

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

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.
     }
 }

使用如下:

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

再Activity里的使用

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


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注入一个Repository对象且Repository中需要当前上下文

//调用方
val viewModel: LoginViewModel by customViewModels()


@Suppress("UNCHECKED_CAST")
class CustomViewModelFactory(private val context: Context) :
    ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(LoginViewModel::class.java)) {
            return LoginViewModel(loginRepository = LoginRepository(context)) as T
        } else if (modelClass.isAssignableFrom(HomeViewModel::class.java)) {
            return HomeViewModel(homeRepository = HomeRepository(context)) 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进行实例化。

记得导入相关的依赖
//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进行实例化。

//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: androidx.lifecycle:lifecycle-viewmodel-android:2.8.0@aar、 Activity: androidx.activity:activity:1.9.0@aar

1、ViewModelStoreOwner

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

/**
 * 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将会被封装到Activity的内部类NonConfigurationInstances中,然后储存到ActivityThread类中的应用数据结构ActityClientRecord中,重建之后,handleReLaunch流程依次经过赋值将NonconfigtionInstaces赋值给ActivityClientRecord,然后接着通过activity.attach将上面赋值重写设置到Activity中,所以还是上次的销毁时的NonCongifurationInstances即ViewModelStore。这才是ViewModel不受配置改变影响的本质。当然后面会用源码分析。

    //储存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即实现了ViewModelStoreOwner的ComponentActivity)。ViewModelProvider 负责将ViewModelProvider.Factory 创建的 ViewModel 实例储存到ViewModelStoreOwner(即ComponentActivity)的ViewModelStore中。

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

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。

xpublic 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实例。
    ```
    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源码如下

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源码如下:

 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接口源码
    
    ```
     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 来判断是否当前 ComponentActivity 的lastNonConfigurationInstance 是否为 null,如果不为空 _viewModelStore = nc.viewModelStore ,否则新建一个_viewModelStore = ViewModelStore()。


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

}

```


### 6Activity

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不是同一个类。

//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源码:

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的本质。

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)) 在发生配置改变前后保证前后一致。

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这里形成闭环。

何时销毁

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

1、正常退出

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

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架构的成熟和发展。开发者更应该了解背后的相关原理,不管在面试还是在使用都胸有成竹。在这里插入图片描述

在这里插入图片描述

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ViewModel 是 Android 架构组件之一,旨在帮助解决 Activity 和 Fragment 与数据持久性相关的问题。ViewModel 类的实例会存储和管理与视图相关的数据,这样即使是配置更改(如旋转屏幕)或者内存回收等情况,数据也不会丢失。ViewModel 通过使用“生命周期感知”来实现这一点,即 ViewModel 的生命周期与 Activity 或 Fragment 的生命周期相关联。 在使用 ViewModel 时,通常的做法是创建一个 ViewModel 子类,将需要管理的数据作为类的成员变量,并提供对应的 get/set 方法。Activity 或 Fragment 可以通过获取 ViewModel 实例并调用其方法来访问和修改数据。 以一个简单的计数器为例,以下是一个使用 ViewModel 的代码示例: 1. 创建 ViewModel 子类 ```kotlin class CounterViewModel : ViewModel() { private var count = 0 fun getCount(): Int { return count } fun incrementCount() { count++ } } ``` 2. 在 Activity 或 Fragment 中获取 ViewModel 实例 ```kotlin val viewModel = ViewModelProviders.of(this).get(CounterViewModel::class.java) ``` 3. 在 Activity 或 Fragment 中使用 ViewModel ```kotlin // 获取计数器的值 val count = viewModel.getCount() // 增加计数器的值 viewModel.incrementCount() ``` 需要注意的是,ViewModel 只能存储与视图相关的数据,不能存储与 Android 系统相关的数据,如 Context、View 等。此外,ViewModel 的生命周期与 Activity 或 Fragment 的生命周期相关联,因此在使用 ViewModel 时也需要确保正确处理生命周期。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值