美文网首页
2-2,ViewModel源码跟踪(java)

2-2,ViewModel源码跟踪(java)

作者: android_小龙 | 来源:发表于2021-06-08 00:24 被阅读0次

1,关于上一节简单使用我自己发出的疑问

1),counterViewModel=new ViewModelProvider(this).get(CounterViewModel.class);一句话实例化viewmodel,google很贴心,但究竟里面做了什么?
2),为什么在屏幕选转重走activity生命周期时候,counterViewModel数据不受影响,counterViewModel到底有没有重新被实例化,数据是怎么维持不变的
3),onCleared()方法在activity被销毁时自动调用是怎么实现的?

2走进代码

问题一:new ViewModelProvider(this).get(CounterViewModel.class)做了啥事

这句代码做了什么
counterViewModel=new ViewModelProvider(this).get(CounterViewModel.class);

我们先看看ViewModelProvider(this)的代码

public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }

ViewModelProvider的构造函数要求接收ViewModelStoreOwner作为参数,ViewModelStoreOwner是一个接口类

public interface ViewModelStoreOwner {
    @NonNull
    ViewModelStore getViewModelStore();
} 
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();
    }
}

看到这里基本可以做出三个猜测
a),ViewModelStore里面维护一个HashMap存储viewmodel类型数据,我们自己编写继承viewmodel的实例可能缓存在ViewModelStore这里
b),可能有个实现ViewModelStoreOwner接口的缓存集合类可以直接getViewModelStore
c),activity的父类必定实现了ViewModelStoreOwner 接口
d),实现了ViewModelStoreOwner的acitivity父类,肯定可以获取到ViewModel并进行相关操作

关于问题d,在ComponentActivity中找到了

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner {

我们继续看看ComponentActivity实现了这个接口后做了什么

@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.");
        }
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
               //从NonConfigurationInstances中恢复ViewModelStore
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

如果mViewModelStore不为空直接返回,要是为空在NonConfigurationInstances不为空的情况下,由NonConfigurationInstances恢复,再不然就直接new了
先看看NonConfigurationInstances类

static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }

里面维护着一个ViewModelStore,且是fianl修饰class,典型的单例模式,这就能解释为何可以直接恢复ViewModelStore了,在看看getLastNonConfigurationInstance()的具体实现

***在Activity基类下***

 @Nullable
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }
static final class NonConfigurationInstances {
        Object activity;
        HashMap<String, Object> children;
        FragmentManagerNonConfig fragments;
        ArrayMap<String, LoaderManager> loaders;
        VoiceInteractor voiceInteractor;
    }
    @UnsupportedAppUsage
    /* package */ NonConfigurationInstances mLastNonConfigurationInstances;

很明显这里是缓存activity,fragment还有其他几个暂时不知道用处的对象, ViewModelStore getViewModelStore()只获取了mLastNonConfigurationInstances.activity信息,
其实这里可以划重点了:

getLastNonConfigurationInstance()这里可以恢复activity保存的最新数据,当然包括viewModelStore

再看看 ViewModelProvider(this).get(CounterViewModel.class);中的get(CounterViewModel.class)方法

 @NonNull
    @MainThread
    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);
    }

具体操作在下面的get()里面

 public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

这段代码也不难理解,如果存在modelClass实例,那就直接返回;否则就创建一个,这里看到了2个mFactory类,OnRequeryFactory类型和KeyedFactory类型,其实KeyedFactory也是继承OnRequeryFactory,但是这两个都实现了Factory 接口

public interface Factory {
        @NonNull
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    }

Factory有2个实现类,分别是NewInstanceFactory和AndroidViewModelFactory ;NewInstanceFactory传入context会引起内存泄漏
回顾下面这段代码

  public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }

image.png

ComponentActivity实现了HasDefaultViewModelProviderFactory这个接口
具体的实现内容是

 @Override
    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        if (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }
*************************************************************************************************************
  @SuppressLint("LambdaLast")
    public SavedStateViewModelFactory(@NonNull Application application,
            @NonNull SavedStateRegistryOwner owner,
            @Nullable Bundle defaultArgs) {
        mSavedStateRegistry = owner.getSavedStateRegistry();
        mLifecycle = owner.getLifecycle();
        mDefaultArgs = defaultArgs;
        mApplication = application;
        mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }

代码最终还是用AndroidViewModelFactory传入application作为context
最后,

 public AndroidViewModelFactory(@NonNull Application application) {
            mApplication = application;
        }

        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                //noinspection TryWithIdenticalCatches
                try {
                    //到头来还是用反射
                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
                } catch (Exception e) {
                    //省略了很多异常抛出
                }
            }
            return super.create(modelClass);
        }

到这里基本可以说明这简单的一句话做了哪些工作了

counterViewModel=new ViewModelProvider(this).get(CounterViewModel.class);

总结:activity的父类实现了ViewModelStoreOwner接口,在具体的实现中维护者一个ViewModelStore对象,缓存着我们自定义Viewmodel的子类,在调用new ViewModelProvider(this)的时候,首先判断activity实现的是NewInstanceFactory还是AndroidViewModelFactory的接口,然后到对应的工厂方法里面查看缓存是否存在实例,有则返回;没有就反射创建一个;

问题二:为什么在屏幕选转重走activity生命周期时候,ViewModel数据不受影

  @Override
    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        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;
    }

上个问题说可以通过NonConfigurationInstances恢复viewModelStore,细看最后几句,NonConfigurationInstances也是再次在这里保存了viewModelStore数据,所以onRetainNonConfigurationInstance() 方法保存了viewModelStore数据;
上个问题也说了getLastNonConfigurationInstance();可以获取到activity保存的最新viewmodelStore数据
可以这样总结:
在旋转屏幕重走屏幕生命周期时,通过getLastNonConfigurationInstance()获取viewModelStore数据,再而从hashmap中取出我们自定义的缓存实例,从而获取到原来的操作状态
所以Android除了onSaveInstanceState()和onRestoreInstanceState()之外,还有onRetainNonConfigurationInstance()和etLastNonConfigurationInstance()这对兄弟,而且这对兄弟存储的信息量可以很大,不受限制
查看资料后发现还有一对姐妹saveManagedDialogs()和restoreManagedDialogs()不过这是应用于对话框的;

问题三:onCleared()方法在activity被销毁时自动调用是怎么实现的?

image.png

很明显ComponentActivity通过lifecycle管理生命周期并在ON_DESTROY回调时执行 getViewModelStore().clear();具体操作是遍历hashmap,之心viewModel.clear();

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

相关文章

网友评论

      本文标题:2-2,ViewModel源码跟踪(java)

      本文链接:https://www.haomeiwen.com/subject/kmcreltx.html