Viewmodel源码分析

2022-06-13/2022-06-14

使用

先在看看我参与过的一个项目(掌上重邮)里的BaseViewModelActivity是怎么初始化Viewmodel的

abstract class BaseViewModelActivity<T : BaseViewModel> : BaseActivity()

    lateinit var viewModel: T

val viewModelFactory = getViewModelFactory()
        val viewModelClass =
            (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
        viewModel = if (viewModelFactory != null) {
            ViewModelProvider(this, viewModelFactory)[viewModelClass]
        } else {
            ViewModelProvider(this)[viewModelClass]
        }

ViewModelProvider的初始化

先来看看ViewModelProvider的初始化

/**
 * An utility class that provides `ViewModels` for a scope.
 *
 * Default `ViewModelProvider` for an `Activity` or a `Fragment` can be obtained
 * by passing it to the constructor: `ViewModelProvider(myFragment)`
 *
 * @param store  `ViewModelStore` where ViewModels will be stored.
 * @param factory factory a `Factory` which will be used to instantiate
 * new `ViewModels`
 */
public open class ViewModelProvider(
    private val store: ViewModelStore,
    private val factory: Factory
){
    /**
     * Creates `ViewModelProvider`, which will create `ViewModels` via the given
     * `Factory` and retain them in a store of the given `ViewModelStoreOwner`.
     *
     * @param owner   a `ViewModelStoreOwner` whose [ViewModelStore] will be used to
     * retain `ViewModels`
     * @param factory a `Factory` which will be used to instantiate
     * new `ViewModels`
     */
    public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
        owner.viewModelStore,
        factory
    )
}

Viewmodel初始化

来看看[]这个操作符重载做了啥

/**
     * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
     * an activity), associated with this `ViewModelProvider`.
     *
     *
     * The created ViewModel is associated with the given scope and will be retained
     * as long as the scope is alive (e.g. if it is an activity, until it is
     * finished or process is killed).
     *
     * @param modelClass The class of the ViewModel to create an instance of it if it is not
     * present.
     * @return A ViewModel that is an instance of the given type `T`.
     * @throws IllegalArgumentException if the given [modelClass] is local or anonymous class.
     */
    @MainThread
    public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
        val canonicalName = modelClass.canonicalName
            ?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
        return get("$DEFAULT_KEY:$canonicalName", modelClass)
    }

internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"

这个操作符重载挺有意思的,是kt的一个语法特性,重载了[]操作符,我们也来写一个试试

fun main(args: Array<String>) {
    val mul = multi(2)
    println(mul[2])
}

class multi(
    private val num1:Int
){

    //重载[]运算符
    operator fun get(num2:Int):Int{
        return num2 * num1
    }
}

//result : 4

我们接着看get做了什么

/**
     * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
     * an activity), associated with this `ViewModelProvider`.
     *
     * The created ViewModel is associated with the given scope and will be retained
     * as long as the scope is alive (e.g. if it is an activity, until it is
     * finished or process is killed).
     *
     * @param key        The key to use to identify the ViewModel.
     * @param modelClass The class of the ViewModel to create an instance of it if it is not
     * present.
     * @return A ViewModel that is an instance of the given type `T`.
     */
    @Suppress("UNCHECKED_CAST")
    @MainThread
    public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
        var viewModel = store[key]
        if (modelClass.isInstance(viewModel)) {
            (factory as? OnRequeryFactory)?.onRequery(viewModel)
            return viewModel as T
        } else {
            @Suppress("ControlFlowWithEmptyBody")
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        viewModel = if (factory is KeyedFactory) {
            factory.create(key, modelClass)
        } else {
            factory.create(modelClass)
        }
        store.put(key, viewModel)
        return viewModel
    }

首先尝试从Viewmodelstore中获取Viewmodel

Viewmodelstore听名字就知道是一个存储viewmodel的类,实际上它里面使用了一个hashmap来存储viewmodel,key为string(即DEFAULT_KEY+canonicalName),value为viewmodel

/**
 * Class to store {@code ViewModels}.
 * <p>
 * An instance of {@code ViewModelStore} must be retained through configuration changes:
 * if an owner of this {@code ViewModelStore} is destroyed and recreated due to configuration
 * changes, new instance of an owner should still have the same old instance of
 * {@code ViewModelStore}.
 * <p>
 * If an owner of this {@code ViewModelStore} is destroyed and is not going to be recreated,
 * then it should call {@link #clear()} on this {@code ViewModelStore}, so {@code ViewModels} would
 * be notified that they are no longer used.
 * <p>
 * Use {@link ViewModelStoreOwner#getViewModelStore()} to retrieve a {@code ViewModelStore} for
 * activities and fragments.
 */
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();
    }
}

所以这里先尝试从store这个缓存中找,没找到的话在尝试下面的自己新建,他这里新建一个viewmodel用的工厂模式

看下工厂的实现

如果我们没有传自定义的工厂,就会使用默认的

public companion object {
            internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
                if (owner is HasDefaultViewModelProviderFactory)
                    owner.defaultViewModelProviderFactory else instance
        }


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

之所以可能要自定义工厂是因为默认的工厂生产viewmodel只支持无参构造,如果我们的viewmodel有参的话就要自定义工厂

可以看到在取默认工厂的时候会判断ViewModelStoreOwner(也就是我们的activity或fragment)是不是HasDefaultViewModelProviderFactory,是的话就返回owner.defaultViewModelProviderFactory,看看HasDefaultViewModelProviderFactory这个接口

/**
 * Interface that marks a {@link ViewModelStoreOwner} as having a default
 * {@link androidx.lifecycle.ViewModelProvider.Factory} for use with
 * {@link androidx.lifecycle.ViewModelProvider#ViewModelProvider(ViewModelStoreOwner)}.
 */
public interface HasDefaultViewModelProviderFactory {
    /**
     * Returns the default {@link androidx.lifecycle.ViewModelProvider.Factory} that should be
     * used when no custom {@code Factory} is provided to the
     * {@link androidx.lifecycle.ViewModelProvider} constructors.
     *
     * @return a {@code ViewModelProvider.Factory}
     */
    @NonNull
    ViewModelProvider.Factory getDefaultViewModelProviderFactory();
}

我们可以通过让owner实现这个接口从而提供自定义的viewmodel

接着看看NewInstanceFactory是怎么生产viewmodel的

public open class NewInstanceFactory : Factory {
        @Suppress("DocumentExceptions")
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return try {
                modelClass.newInstance()
            } catch (e: InstantiationException) {
                throw RuntimeException("Cannot create an instance of $modelClass", e)
            } catch (e: IllegalAccessException) {
                throw RuntimeException("Cannot create an instance of $modelClass", e)
            }
        }
}

和我们刚才说的一样,默认通过反射无参构造构建新对象

Viewmodel怎么实现它的特性的

Viewmodel的生命周期比activity长,所以能做到旋转屏幕后仍然可以留存数据,这是怎么做到的呢,简单说一下

获取ViewModel时首先尝试从ViewModelStore中获取,ViewModelStore中如果获取不到就通过工厂去反射创建ViewModel并存储到ViewModel中,默认提供的工厂只能创建无参构造的ViewModel,如果要使用有参构造的ViewModel则要自己去实现工厂,ViewModelStore是通过Activity或Fragment去获取的,这俩个类都实现了getViewModelStore接口用来提供ViewModelStore,在Activity的getViewModelStore中会先通过NonConfigurationInstance获取 ViewModelStore实例,如果NonConfigurationInstance不存在,就new一个ViewModelStore

然后在Activity的onRetainNonConfigurationInstance方法中,会把这个ViewModelStore赋值给NonConfigurationInstance,而这个onRetainNonConfigurationInstance方法方法调用的时机是在Activity的onStop生命周期方法与onDestory生命周期方法之间,也就是说当Activity配置改变时这个方法会被调用,而这个NonConfigurationInstance可以在Activity被重建后通过getLastNonConfigurationInstance方法拿到,从而拿到了ViewModelStore,从而又拿到了ViewModel,还有就是为什么NonConfigurationInstance为什么不会随着Activity的销毁而销毁呢?因为这个NonConfigurationInstance被保存在ActivityClientRecord里面,然后ActivityClientRecord又被保存在安卓主线程ActivityThread中的一个ArrayMap中,从而NonConfigurationInstance不会随着Activity的销毁而销毁。在创建Activity的performLaunchActivity中,会拿到ActivityClientRecord然后创建Activity的上下文Context进行attach绑定。

tips:ViewModelStores.of(FragmentActivity)需要区分下Support Library的版本,如果在v27.1.0版本及以上的,FragmentActivity已经实现了ViewModelStoreOwner接口,最终是通过FragmentActivity.onRetainNonConfigurationInstance()完成ViewModelStore的存储的,v27.1.0版本以下才是通过Fragment.setRetainInstance(true)实现。

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

秘诀就在这里的owner.viewModelStore

我们看看ComponentActivity,它实现了ViewModelStoreOwner

我们把跟Viewmodelstore相关的代码都展示出来

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ContextAware,
        LifecycleOwner,
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner,
        ActivityResultRegistryOwner,
        ActivityResultCaller {
        
   static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }
    
    private ViewModelStore mViewModelStore;
    
        public ComponentActivity() {
           Lifecycle lifecycle = getLifecycle();
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    // Clear out the available context
                    mContextAwareHelper.clearAvailableContext();
                    // And clear the ViewModelStore
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });
        }
        

    /**
     * {@inheritDoc}
     * <p>
     * Overriding this method is no longer supported and this method will be made
     * <code>final</code> in a future version of ComponentActivity. If you do override
     * this method, you <code>must</code>:
     * <ol>
     *     <li>Return an instance of {@link LifecycleRegistry}</li>
     *     <li>Lazily initialize your LifecycleRegistry object when this is first called.
     *     Note that this method will be called in the super classes' constructor, before any
     *     field initialization or object state creation is complete.</li>
     * </ol>
     */
    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }

    /**
     * Returns the {@link ViewModelStore} associated with this activity
     * <p>
     * Overriding this method is no longer supported and this method will be made
     * <code>final</code> in a future version of ComponentActivity.
     *
     * @return a {@code ViewModelStore}
     * @throws IllegalStateException if called before the Activity is attached to the Application
     * instance i.e., before onCreate()
     */
    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        ensureViewModelStore();
        return mViewModelStore;
    }

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void ensureViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }

我们沿着这个NonConfigurationInstance的保存和获取接着分析下去

getLastNonConfigurationInstance

/**
     * Retrieve the non-configuration instance data that was previously
     * returned by {@link #onRetainNonConfigurationInstance()}.  This will
     * be available from the initial {@link #onCreate} and
     * {@link #onStart} calls to the new instance, allowing you to extract
     * any useful dynamic state from the previous instance.
     *
     * <p>Note that the data you retrieve here should <em>only</em> 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 {@link #onSaveInstanceState(Bundle)} mechanism) even if this
     * function returns null.
     *
     * <p><strong>Note:</strong> For most cases you should use the {@link Fragment} API
     * {@link Fragment#setRetainInstance(boolean)} instead; this is also
     * available on older platforms through the Android support libraries.
     *
     * @return the object previously returned by {@link #onRetainNonConfigurationInstance()}
     */
    @Nullable
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

从注释中可以看到是在onRetainNonConfigurationInstance进行保存的

/**
     * Retain all appropriate non-config state.  You can NOT
     * override this yourself!  Use a {@link androidx.lifecycle.ViewModel} if you want to
     * retain your own non config state.
     */
    @Override
    @Nullable
    @SuppressWarnings("deprecation")
    public final Object onRetainNonConfigurationInstance() {
        // Maintain backward compatibility.
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last NonConfigurationInstance
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }

        if (viewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }

来看看Android官方中关于onRetainNonConfigurationInstance的解释

Called by the system, as part of destroying an activity due to a configuration change, when it is known that a new instance will immediately be created for the new configuration. You can return any object you like here, including the activit
instance itself, which can later be retrieved by calling getLastNonConfigurationInstance() in the new activity instance. If you are targeting Build.VERSION_CODES.HONEYCOMB or later, consider instead using a Fragment with Fragment.setRetainInstance(boolean.
This function is called purely as an optimization, and you must not rely on it being called. When it is called, a number of guarantees will be made to help optimize configuration switching:
The function will be called between onStop and onDestroy.
A new instance of the activity will always be immediately created after this one's onDestroy() is called. In particular, no messages will be dispatched during this time (when the returned object does not have an activity to be associated with).
The object you return here will always be available from the getLastNonConfigurationInstance() method of the following activity instance as described there.
These guarantees are designed so that an activity can use this API to propagate extensive state from the old to new activity instance, from loaded bitmaps, to network connections, to evenly actively running threads. Note that you should not propagate any data that may change based on the configuration, including any data loaded from resources such as strings, layouts, or drawables.
The guarantee of no message handling during the switch to the next activity simplifies use with active objects. For example if your retained state is an android.os.AsyncTask you are guaranteed that its call back functions (like android.os.AsyncTask.onPostExecute) will not be called from the call here until you execute the next instance's onCreate(Bundle). (Note however that there is of course no such guarantee for android.os.AsyncTask.doInBackground since that is running in a separate thread.)
Note: For most cases you should use the Fragment API Fragment.setRetainInstance(boolean) instead; this is also available on older platforms through the Android support libraries.
Returns:
any Object holding the desired state to propagate to the next activity instance


标题:Viewmodel源码分析
作者:OkAndGreat
地址:http://zhongtai521.wang/articles/2022/06/13/1655106585380.html

评论
发表评论
       
       
取消