美文网首页
Jetpack源码解析(三):ViewModel解析

Jetpack源码解析(三):ViewModel解析

作者: 慕尼黑凌晨四点 | 来源:发表于2020-12-24 14:28 被阅读0次

    Jetpack源码解析之ViewModel

    ViewModel 最早是在 2017 年的 Google I/O 大会上被提出,同时间问世的还有 Lifecycle 和 LiveData。

    Google开发团队希望我们用ViewModel来管理页面状态相关信息,并建议我们搭配LiveData一起食用。

    基本使用

    依赖(可选),activity-ktx artifact。

    implementation "androidx.fragment:fragment-ktx:1.2.5"//by viewModels()
    

    ViewModel和Activity/Fragment相绑定,被视图持有。ViewModel存在的时间范围是从首次请求ViewModel直到 Activity 完成并销毁,所以ViewModel类让数据可在发生屏幕旋转等配置更改后继续留存。

        class MyViewModel : ViewModel() {
            private val users: MutableLiveData<List<User>> by lazy {
                MutableLiveData().also {
                    loadUsers()
                }
            }
    
            fun getUsers(): LiveData<List<User>> {
                return users
            }
    
            private fun loadUsers() {
                // Do an asynchronous operation to fetch users.
            }
        }
        
    

    然后,您可以从 Activity 访问该列表,如下所示:

        class MyActivity : AppCompatActivity() {
    
            override fun onCreate(savedInstanceState: Bundle?) {
                // Create a ViewModel the first time the system calls an activity's onCreate() method.
                // Re-created activities receive the same MyViewModel instance created by the first activity.
    
                // Use the 'by viewModels()' Kotlin property delegate
                // from the activity-ktx artifact
                val model: MyViewModel by viewModels()
                //等同于 val model = ViewModelProvider(this).get(MyViewModel::class.java)
                model.getUsers().observe(this, Observer<List<User>>{ users ->
                    // update UI
                })
            }
        }
        
    

    注意ViewModel 绝不能引用视图、Lifecycle 或可能存储对 Activity 上下文的引用的任何类。

    by viewModels()为Kotlin语法糖,Fragment中用by viewModels获取到的是属于该Fragment的model,如果在Fragment中想要获取activity的ViewModel,则要使用 by activityViewModels()

    原理解析

    ViewModel创建

    by viewModels()是ktx包的便便用法,实际内部调用到的还是ViewModelProvider(store, factory).get(viewModelClass.java)。所以我们直接看ViewModelProvider。

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
             ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
             : NewInstanceFactory.getInstance());
    }
    
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }
    

    传入了store和factory。

    store

    getViewModelStore()获取的。ComponentActivity实现了ViewModelStoreOwner接口,所以getViewModelStore的实现在ComponentActivity中,每个Activity中会持有一个viewModel实例。

    compomentActivity.getViewModelStore()

    @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
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
    

    factory

    public interface Factory {
        /**
        * Creates a new instance of the given {@code Class}.
        * <p>
        *
        * @param modelClass a {@code Class} whose instance is requested
        * @param <T>        The type parameter for the ViewModel.
        * @return a newly created ViewModel
        */
        @NonNull
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    }
    

    可以看到Factory就一个create方法,注释有说用来创建ViewModel实例的。

    HasDefaultViewModelProviderFactory

    如果activity实现了HasDefaultViewModelProviderFactory接口,则factory在getDefaultViewModelProviderFactory中:

    @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;
    }
    
    public final class SavedStateViewModelFactory extends ViewModelProvider.KeyedFactory  { ... }
    abstract static class KeyedFactory extends OnRequeryFactory implements Factory { ... }
    

    返回的是个SavedStateViewModelFactory,其create方法:

    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        // ViewModelProvider calls correct create that support same modelClass with different keys
        // If a developer manually calls this method, there is no "key" in picture, so factory
        // simply uses classname internally as as key.
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return create(canonicalName, modelClass);
    }
    
    private static final Class<?>[] ANDROID_VIEWMODEL_SIGNATURE = new Class[]{Application.class,SavedStateHandle.class};
    private static final Class<?>[] VIEWMODEL_SIGNATURE = new Class[]{SavedStateHandle.class};
    
    @Override
    public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
        //AndroidViewModel 继承ViewModel,内部持有一个application
        boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
        //判断是不是AndroidViewModel的子类,用不同的构造器
        Constructor<T> constructor;
        if (isAndroidViewModel) {
            constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
        } else {
            constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
        }
        // doesn't need SavedStateHandle
        if (constructor == null) {
            //到这里表示是无参数的构造函数,会调到NewInstanceFactory中的create,和第二种情况一样
            return mFactory.create(modelClass);
        }
    
        SavedStateHandleController controller = SavedStateHandleController.create(
                mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
        try {
            T viewmodel;
            if (isAndroidViewModel) {
                viewmodel = constructor.newInstance(mApplication, controller.getHandle());
            } else {
                viewmodel = constructor.newInstance(controller.getHandle());
            }
            viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
            return viewmodel;
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Failed to access " + modelClass, e);
        } catch (InstantiationException e) {
            throw new RuntimeException("A " + modelClass + " cannot be instantiated.", e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException("An exception happened in constructor of "
                    + modelClass, e.getCause());
        }
    }
    

    先判断传入的类是不是AndroidViewModel的子类,然后调用findMatchingConstructor,传入的signature不同。

    private static final Class<?>[] ANDROID_VIEWMODEL_SIGNATURE = new Class[]{Application.class,SavedStateHandle.class};
    private static final Class<?>[] VIEWMODEL_SIGNATURE = new Class[]{SavedStateHandle.class};
    
    private static <T> Constructor<T> findMatchingConstructor(Class<T> modelClass,
            Class<?>[] signature) {
        for (Constructor<?> constructor : modelClass.getConstructors()) {
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            if (Arrays.equals(signature, parameterTypes)) {
                return (Constructor<T>) constructor;
            }
        }
        return null;
    }
    

    这堆代码说白了就是:要么返回一个形参为SavedStateHandle.class的构造函数,要么返回一个形参为(Application.class,SavedStateHandle.class)的构造函数。没有这两样构造函数的中的任何一种的话,返回null。如果为null,则处理方式和第二种情况一样了。

    如果不为null,则证明构造函数中传入了SavedStateHandle,即需要保存状态。后续代码暂且不表。

    !HasDefaultViewModelProviderFactory

    如果activity没有实现HasDefaultViewModelProviderFactory接口,返回的直接是个NewInstanceFactory。且NewInstanceFactory中,其create方法如下:

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        //noinspection TryWithIdenticalCatches
        try {
            return modelClass.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        }
    }
    

    直接拿到类后通过反射就new出了Instance。

    get(VM.class)

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

    如果能在mViewModelStore中通过“包名”为Key获取到viewModel则将其返回;否则用上一步创造出来的Factory来创建viewModel,然后存入mViewModelStore中。

    ViewModelStore

    通过以上的描述,猜想ViewModelStore应该是个类似map的东西。所以看下源码:

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

    果然,内部维护了一个map,Key就是我们class的完整包名,在同一个activity下重复获取获取到的都是是同一个viewModel。

    ViewModel销毁

    ViewModel销毁的相关代码在ComponentActivity的初始化方法中:

    public ComponentActivity() {
        ...
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });
    }
    

    利用LifecycleObserver,当执行ON_DESTROY方法时,调用到viewModelStore的clear()。

    保存状态

    ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle。

    ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 Activity,是在 Activity Finish时;而对于 Fragment,是在 Fragment 分离时。

    说明 ViewModel 随着 Activity 状态的改变而经历的生命周期。

    有一种情况比较特殊,屏幕旋转(不配置configChanges)的时候,activity生命周期是会经过销毁重建的,即:

    onPause--onSaveInstanceState--onStop--onRetainCustomNonConfigurationInstance--onDestroy--onCreate--onStart--onRestoreInstanceState--onResume

    而ViewModel不会再onDestroy过程中销毁,原因有二:

    1. 在onDestroy判断中,有一个判断:
    if (!isChangingConfigurations()) {
        getViewModelStore().clear();
    }
    

    isChangingConfigurations表示此次销毁是否是为了改变配置而重建的一次操作,如果是的,就先不销毁了。

    2.NonConfigurationInstances

    我们在onStop和onDestroy中会调用一个onRetainCustomNonConfigurationInstance【为了便于区分简写onRCustomNC】方法,这个方法原是为了保留自定义非配置实例,它的前身是onRetainNonConfigurationInstance【简写为onRNC】,但是现在onRNC不能被重写【加了final】,onRCustomNC即将被弃用,官方推荐我们用ViewModel来代替。

    因为在onRetainNonConfigurationInstance【onRNC】中,源码中将mViewModelStore保存了起来。

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

    这里面用来保存viewModelStore的载体是NonConfigurationInstances。NonConfigurationInstances是ComponentActivity中的一个静态内部类,里面存了viewModelStore;

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

    也就是在destory之前,viewmodelStory就已被保存了。

    再回顾getViewModelStore方法,在获取viewModelStore时,新建之前,从NonConfigurationInstances的中试图获取viewmodelStory;

    if (mViewModelStore == null) {
        NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    

    这里的nc便是上面保存的nci。

    面试问题

    viewModelStore何时初始化

    调试过程中无意发现,就算你新建一个Activity什么都不做,也会调用到getViewModelStore方法,其原因在于viewModelStory在onCreate中就被初始化了。在FragmentActivity中:

    final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
    
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        mFragments.attachHost(null /*parent*/);
        ...
    }
    

    FragmentControl:

    final FragmentManager mFragmentManager = new FragmentManagerImpl();
    
    public void attachHost(@Nullable Fragment parent) {
        mHost.mFragmentManager.attachController(
                mHost, mHost /*container*/, parent);
    }
    

    FragmentManager:

    void attachController(@NonNull FragmentHostCallback<?> host,
            @NonNull FragmentContainer container, @Nullable final Fragment parent) {
        if (mHost != null) throw new IllegalStateException("Already attached");
        mHost = host;
        mContainer = container;
        mParent = parent;
        if (mParent != null) {
            // Since the callback depends on us being the primary navigation fragment,
            // update our callback now that we have a parent so that we have the correct
            // state by default
            updateOnBackPressedCallbackEnabled();
        }
        // Set up the OnBackPressedCallback
        if (host instanceof OnBackPressedDispatcherOwner) {
            OnBackPressedDispatcherOwner dispatcherOwner = ((OnBackPressedDispatcherOwner) host);
            mOnBackPressedDispatcher = dispatcherOwner.getOnBackPressedDispatcher();
            LifecycleOwner owner = parent != null ? parent : dispatcherOwner;
            mOnBackPressedDispatcher.addCallback(owner, mOnBackPressedCallback);
        }
    
        // Get the FragmentManagerViewModel
        if (parent != null) {
            mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
        } else if (host instanceof ViewModelStoreOwner) {
            //这里,已经被初始化了
            ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
            mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
        } else {
            mNonConfig = new FragmentManagerViewModel(false);
        }
    }
    

    相关文章

      网友评论

          本文标题:Jetpack源码解析(三):ViewModel解析

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