随笔博文

关于ViewModel的这几个问题,你都知道吗?

2022-12-20 12:38:31 michael007js 99

前言

作为一名Android开发者,如果你熟悉MVVM架构,熟悉Jetpack组件,那么相信你肯定使用过ViewModel

正如它的名字一样,它是Google推出的一个类,方便我们实现MVVM架构中的ViewModel层。我们在其中处理View层所需的数据,然后在特定条件下通知View层进行UI更新

正如官方所介绍:

ViewModel 类以注重生命周期的方式存储和管理界面相关的数据ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存

我们抓一下这句话的重点:

  1. 注重生命周期的方式:会在合适的时间进行自我回收,防止出现内存泄漏。

  2. 存储和管理界面相关的数据:符合MVVM架构ViewModel层的理念。

  3. 在发生屏幕旋转等配置更改后继续留存数据:为什么要这么设计?怎么做到的。

接下来,就让我们带着问题,深入学习一下ViewModel类。

使用方法

在阅读源码前,让我们先来简单回顾一下ViewModel的使用方法。

class MainViewModel(private val repository: MainRepo) : ViewModel() {

   private val _textMld = MutableLiveData<String>()
   val textLd: LiveData<String> = _textMld
   
   fun getTextInfo() {
       viewModelScope.launch {
           withContext(Dispatchers.IO) {
               //做异步网络请求工作,获取到textData
               repository.getTextInfo()
         }.apply {
               _textMld.postValue(textData)
         }
     }
 }
}

class MainActivity : AppCompatActivity() {

   fun setVmFactory(): ViewModelProvider.Factory {
       return object : ViewModelProvider.Factory {
           override fun <T : ViewModel> create(modelClass: Class<T>): T {
               return MainViewModel(MainRepo()) as T
         }
     }
 }

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       val binding = ActivityMainBinding.inflate(layoutInflater)
       setContentView(binding.root)
       
       val vm = ViewModelProvider(this, setVmFactory())[MainViewModel::class.java]
       vm.textLd.observe(this, Observer {
           binding.textTv.text = it
     })
 }
}

方法步骤可以简单分为两步,分别为:

  1. 继承ViewModel类实现自定义ViewModel,如:MainViewModel。

  2. 通过ViewModelProvider实例化ViewModel

源码

现在,根据上方所介绍的使用方法,我们进一步来看一下ViewModel的源码。

public abstract class ViewModel {
 ....

   @SuppressWarnings("WeakerAccess")
   protected void onCleared() {
 }

   @MainThread
   final void clear() {
 
     ....

       onCleared();
 }

 ....
}

ViewModel 是一个抽象类,提供 onCleared()方法供我们在ViewModel销毁前做一下清除工作。

接下来,我们来看看是如何通过ViewModelProvider来实例化ViewModel对象的。

其实就是两步:

  1. 实例化一个 ViewModelProvider 对象。

  2. 调用 ViewModelProvider.get()方法来得到一个ViewModel对象。

先来看看它的构造函数:

public open class ViewModelProvider(
   private val store: ViewModelStore,
   private val factory: Factory
) {

public constructor(
   owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner))

public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
   owner.viewModelStore,
   factory
)

......

}

通过 ViewModelProvider 的构造函数可以看出,一共有两个参数:ViewModelStoreFactory

分别来看看这两个参数代表着什么意思。

public class ViewModelStore {

   private final HashMap<String, ViewModel> mMap = new HashMap<>();

   final void put(String key, ViewModel viewModel) {
//所以这里key需要注意,不要使用相同的key,否则后创建的VM会替换到老的VM
       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());
 }

   public final void clear() {
       for (ViewModel vm : mMap.values()) {
           //调用ViewModel的clear方法,表示不再使用
           vm.clear();
     }
//清除集合中的所有ViewModel
       mMap.clear();
 }
}

ViewModelStore:正如其名,就是用来存储ViewModel对象的,通过内部维护一个HashMap来实现对ViewMoel对象的存储与管理工作。

再来看看上面所介绍的 Factory

而Factory则是工厂接口的,用来实例化ViewModel,具体实现可以参考使用方法中所介绍的 setVmFactory() 方法,用来实例化 MainViewModel。

public interface Factory {

   public fun <T : ViewModel> create(modelClass: Class<T>): T

}

接下来,我们来看看 ViewModelProvider 中的 get() 方法。

public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
   val canonicalName = modelClass.canonicalName
   return get("$DEFAULT_KEY:$canonicalName", modelClass)
}

public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
   //1.根据key从ViewModelStore中取出ViewModel
   var viewModel = store[key]

   //2.通过工厂方法来实例化viewModel
   if (modelClass.isInstance(viewModel)) {
     (factory as? OnRequeryFactory)?.onRequery(viewModel)
       return viewModel as T
 }
   
   viewModel = if (factory is KeyedFactory) {
       factory.create(key, modelClass)
 } else {
       factory.create(modelClass)
 }

   //3.将实例化的ViewModel放入ViewModelStore
   store.put(key, viewModel)
   return viewModel
}

概括一下 get() 方法:

  1. 根据传入的形参Class.canonicalName做为key,从ViewModelStore中取出ViewModel。

  2. 再通过工厂方法来实例化ViewModel。

  3. 最后将实例化后的ViewModel放入ViewModelStore中,并返回。

OK,根据使用方法来分析源码的话,我们好像已经分析完了

首页
关于博主
我的博客
搜索