欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android Architecture Component之:深层次理解ViewModel

程序员文章站 2022-06-07 16:54:06
...

上一篇我们从源码层面上分析了LiveData的内部实现,今天我们来走进ViewModel的内心。

  1. 回顾如何获取一个ViewModel实例:

    ViewModelProvider(this, MainViewModelFactory())[MainViewModel::class.java]

    那么我们可以从ViewModelProvider的构造器入手。

  2. ViewModelProvider类:

    Android Architecture Component之:深层次理解ViewModel

    我们都是使用ComponentActivity或者Fragment作为第一个参数,他们都实现了ViewModelStoreOwner接口,所以我们需要关注上面这个构造方法。我们这里查看ComponentActivity类对于getViewModelStore()方法的实现:

    Android Architecture Component之:深层次理解ViewModel

  3. getLastNonConfigurationInstance()方法:

    /*

    Retrieve the non-configuration instance data that was previously returned by onRetainNonConfigurationInstance(). This will be available from the initial onCreate and onStart calls to the new instance, allowing you to extract any useful dynamic state from the previous instance. Note that the data you retrieve here should only be used as an optimization for handling configuration changes. You should always be able to handle getting a null pointer back, and an activity must still be able to restore itself to its previous state (through the normal onSaveInstanceState(Bundle) mechanism) even if this function returns null.

    */

    onRetainNonConfigurationInstance()方法:

    Android Architecture Component之:深层次理解ViewModel

    会发现在onRetainNonConfigurationInstance()方法,创建了NonConfigurationInstances实例,将viewModelStore存储在其中,最终作为结果值返回。

    回顾一下我们目前为止整个的分析流程(伪代码):

    ViewModelProvider constructor{
    
    ​     ComponentActivity.getViewModelStore(){
    
    ​         getLastNonConfigurationInstance(); 
    
    ​             (从这个方法我们会联系到onRetainNonConfigurationInstance()方法)
    
    ​     }
    
    }

    换句话说,目前为止的分析都只是在ViewModelProvider的构造器中,我们知道ViewModelStore以及Factory相关信息已经存储在了ViewModelProvider中。

  4. ViewModelProvider.get()方法:

    Android Architecture Component之:深层次理解ViewModel

    Android Architecture Component之:深层次理解ViewModel

    通过上面的源码分析,ViewModel的实例实际上是存储在ViewModelStore中的。

    • viewModel不为null:返回该实例
    • viewModel为null:调用factory的create方法,并将create方法返回的ViewModel实例再次put到ViewModelStore中去,最后才将实例对象返回。
  5. ViewModelStore类:

    store,译作商店,存储;那么ViewModelStore可以被认为是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();
       }
    }

    这是ViewModelStore类所有的代码,不超过30行,就一个HashMap,键是一个String类型,值则是ViewModel实例。我们回顾一下ViewModelProvider.get()方法,看看键到底长什么样:

    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
       String canonicalName = modelClass.getCanonicalName();
       if (canonicalName == null) {
           throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
       }
       return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
    private static final String DEFAULT_KEY =
           "androidx.lifecycle.ViewModelProvider.DefaultKey";

    DEFAULT_KEY + ":" + modelClass.canonicalName,组成了key。

    至于,我们对于ViewModelProvider还有ViewModelStore的工作原理大致理清楚了。

  6. ViewModel类自身:

    public abstract class ViewModel {
    
       @Nullable
       private final Map<String, Object> mBagOfTags = new HashMap<>();
       private volatile boolean mCleared = false;
    
       @SuppressWarnings("WeakerAccess")
       protected void onCleared() {}
    
       @MainThread
       final void clear() {
           mCleared = true;
           if (mBagOfTags != null) {
               synchronized (mBagOfTags) {
                   for (Object value : mBagOfTags.values()) {
                       // see comment for the similar call in setTagIfAbsent
                       closeWithRuntimeException(value);
                   }
               }
           }
           onCleared();
       }
    
       @SuppressWarnings("unchecked")
       <T> T setTagIfAbsent(String key, T newValue) {
           T previous;
           synchronized (mBagOfTags) {
               previous = (T) mBagOfTags.get(key);
               if (previous == null) {
                   mBagOfTags.put(key, newValue);
               }
           }
           T result = previous == null ? newValue : previous;
           if (mCleared) {
               closeWithRuntimeException(result);
           }
           return result;
       }
    
       @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
       <T> T getTag(String key) {
           if (mBagOfTags == null) {
               return null;
           }
           synchronized (mBagOfTags) {
               return (T) mBagOfTags.get(key);
           }
       }
    
       private static void closeWithRuntimeException(Object obj) {
           if (obj instanceof Closeable) {
               try {
                   ((Closeable) obj).close();
               } catch (IOException e) {
                   throw new RuntimeException(e);
               }
           }
       }
    }

    这是我把代码中注释删掉后,ViewModel类所有的源码。我们只需要关注两点:

    • mBagOfTags:这是一个HashMap,然后围绕这个HashMap的两个方法:setTagIfAbsent()和getTag()分别用于往该HashMap中存储,而getTag()则根据key来获取value。也就是说,我们可以通过这两个方法往ViewModel对象存取信息。可能你在项目中并没有使用过,别急,我们待会看看viewModelScope的实现。

    • clear()方法:onCleared()是留给开发者去重写从而实现自己的一些清理工作。

      final void clear() {
       mCleared = true;
       if (mBagOfTags != null) {
           synchronized (mBagOfTags) {
               for (Object value : mBagOfTags.values()) {
                   // see comment for the similar call in setTagIfAbsent
                   closeWithRuntimeException(value);
               }
           }
       }
       onCleared();
      }
      
      private static void closeWithRuntimeException(Object obj) {
           if (obj instanceof Closeable) {
               try {
                   ((Closeable) obj).close();
               } catch (IOException e) {
                   throw new RuntimeException(e);
               }
           }
       }

      会把mBagOfTags的值都拿出来遍历一遍,如果是Closeable接口的子类,则调用close()方法,关闭资源。

  7. 在ViewModel中使用协程,我们都会viewModelScope这个扩展属性来启动一个子协程,例如我们在前一篇

    Android Architecture Component之:深层次理解LiveData 中:

    viewModelScope.launch {
       repeat(5) {
           delay(2000)
           periodTextData.value = "count: $it"
       }
    }

    我们为什么会使用这个viewModelScope属性呢?它到底是个啥,我们来看它的实现:

    private const val JOB_KEY = "androidx.lifecycle.ViewModelCoroutineScope.JOB_KEY"
    
    /**
    * [CoroutineScope] tied to this [ViewModel].
    * This scope will be canceled when ViewModel will be cleared, i.e [ViewModel.onCleared] is called
    *
    * This scope is bound to
    * [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate]
    */
    val ViewModel.viewModelScope: CoroutineScope
           get() {
               val scope: CoroutineScope? = this.getTag(JOB_KEY)
               if (scope != null) {
                   return scope
               }
               return setTagIfAbsent(JOB_KEY,
                   CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
           }
    
    internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
       override val coroutineContext: CoroutineContext = context
    
       override fun close() {
           coroutineContext.cancel()
       }
    }
    • viewModelScope属性的get():会调用我们前面提到的getTag方法以及setTagIfAbsent方法。
    • CloseableCoroutineScope类:该类实现了两个接口,Closeable和CoroutineScope。这个Closeable接口是不是刚刚在closeWithRuntimeException方法里碰到过。那么也就是说,ViewModel的clear()方法里,会调用CloseableCoroutineScope里的close方法:coroutineContext.cancel(),从而使得由viewModelScope启动所有子协程都被取消了,避免了泄漏。
  8. ViewModel的clear()方法什么时候会被调用?

    我们只需要查看androidx.activity.ComponentActivity的构造方法:

    Android Architecture Component之:深层次理解ViewModel

    一目了然,注册了一个Observer,当接收到ON_DESTROY事件时,会调用当前ComponentActivity对应的ViewModelStore对象的clear方法:

    public final void clear() {
       for (ViewModel vm : mMap.values()) {
           vm.clear();
       }
       mMap.clear();
    }

    从而就会调用对应ViewModel实例的clear方法。对于addObserver方法的分析,见 前一篇 对于LiveData的源码分析。

  9. 对于Fragment而言,分析是类似,其对应的mViewModelStores是在一个叫FragmentManagerViewModel类中,该类本身会继承自ViewModel类。有了前面的分析基础,大家也能够轻松对该类进行分析,这里就不在赘述了。