ViewModel

作者: 涛涛123759 | 来源:发表于2021-09-10 16:16 被阅读0次

    Android知识总结

    一、什么是ViewModel

    ViewModel类指在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。在对应的作用域内,保正只生产出对应的唯一实例,保证UI组件间的通信。

    由于 ViewModel 生命周期可能长与Activity 生命周期,所以为了避免内存泄漏,Google 禁止在 ViewModel 中持有 Context 、Activity 或 View 的引用。如果一定需要使用 Context, 可以继承 AndroidViewModel 类,内部维护了一个 ApplicationContext

    ViewModel 一般要配合 LiveDataDataBinding一起使用

    • 特点
      1)、通过定义我们可以得出

    • ViewModel不会随着Activity的屏幕旋转而销毁;

    • 在对应的作用域内,保正只生产出对应的唯一实例,保证UI组件间的通信
      2)、重点说一下ViewModel和onSaveInstanceState的关系

    • 对于简单的数据,Activity 可以使用 onSaveInstanceState() 方法从 onCreate() 中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。

    • ViewModel存储大量数据,不用序列化与反序列化

    • onSaveInstanceState存储少量数据

    • 相辅相成,不是替代

    • 进程关闭是onSaveInstanceState的数据会保留,而ViewModel销毁

    二、ViewModel中各个类

    • ViewModelStoreOwner:是一个接口,用来获取一个ViewModelStore对象
    • ViewModelStore存储多个ViewModel,一个ViewModelStore的拥有者( Activity )在配置改变, 重建的时候,依然会有这个实例
    • ViewModel:一个对 Activity、Fragment 的数据管理类,通常配合 LiveData 使用
    • ViewModelProvider创建一个 ViewModel 的实例,并且在给定的ViewModelStoreOwner中存储 ViewModel

    三、源码分析

    • 1)、ViewModelProvider
      ViewModelProvider构造方法
    private final Factory mFactory;
    private final ViewModelStore mViewModelStore;
    
    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;
    }
    
    • 通过 ViewModelStoreOwner获取ViewModelStore对象并给 mViewModelStore赋值
    • 给mFactory赋值,这里赋值的是NewInstanceFactory这个对象

    ViewModelProvider的 get 方法

    private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey";
    
    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");
        }
        //用来获取对应 ViewModel 实例的,保证了同一个 Key 取出是同一个 ViewModel
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
    
    //ViewModelProvider.java
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        //从ViewModelStore中,根据 key,取一个 ViewModel
        ViewModel viewModel = mViewModelStore.get(key);
        //判断取出来的 ViewModel 实例和传进来的是否是一个,是同一个,直接返回此缓存中实例
        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.
            }
        }
        //通过Factory创建一个ViewModel
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        //把新创建的ViewModel用ViewModelStore存储起来,以备下次使用,最后返回新创建的ViewModelStore
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }
    
    • 2)、ViewModelStore的源码
    public class ViewModelStore {
         //声明一个 Map 来存储ViewModel
        private final HashMap<String, ViewModel> mMap = new HashMap<>();
         //存储ViewModel
        final void put(String key, ViewModel viewModel) {
            ViewModel oldViewModel = mMap.put(key, viewModel);
            if (oldViewModel != null) {
                oldViewModel.onCleared();
            }
        }
        //取出 ViewModel
        final ViewModel get(String key) {
            return mMap.get(key);
        }
    
        Set<String> keys() {
            return new HashSet<>(mMap.keySet());
        }
    
    • 3)、NewInstanceFactory
      通过反射,直接创建了ViewModel对象
    //ViewModelProvider.java 中的 AndroidViewModelFactory.java
    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);
        }
    }
    
    • 4)、ViewModelStoreOwner
    public interface ViewModelStoreOwner {
        /**
         * Returns owned {@link ViewModelStore}
         *
         * @return a {@code ViewModelStore}
         */
        @NonNull
        ViewModelStore getViewModelStore();
    }
    

    一个接口,里面一个方法返回了ViewModelStore对象,它的实现类在 AndroidX 中ComponentActivity和 Fragment。

    public class ComponentActivity extends androidx.core.app.ComponentActivity implements ViewModelStoreOwner,XXX{
       
        private ViewModelStore mViewModelStore;
    
        @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
                    mViewModelStore = nc.viewModelStore;
                }
                //创建了一个ViewModelStore并返回了
                if (mViewModelStore == null) {
                    mViewModelStore = new ViewModelStore();
                }
            }
            return mViewModelStore;
        } 
    }
    

    四、总结

    • 1)、分析为啥ViewModel不会随着Activity的屏幕旋转而销毁

    首先知道的是 ViewModel 不被销毁,是在一个 ViewModelStore 的 Map 中存着呢,所以要保证ViewModelStore不被销毁。

    主要是通过onRetainNonConfigurationInstance方法把ViewModelStore缓存;在NonConfigurationInstances中,在getViewModelStore取出ViewModelStore。用NonConfigurationInstancesViewModelStore存储和读取。

    • onRetainNonConfigurationInstance方法存储
    static final class NonConfigurationInstances {
        Object custom;
        //存储 ViewModel
        ViewModelStore viewModelStore;
    }
    
    @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;
        }
        //1把ViewModel存储在 NonConfigurationInstances 对象中
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }
    
    • getViewModelStore方法获取
    @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) {
            //1获取了NonConfigurationInstances一个对象,不为空从其身上拿一个ViewModelStore,这个就是之前保存的ViewModelStore
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
    

    当 Activity 重建时还会走到getViewModelStore方法,这时候就是在NonConfigurationInstances拿一个缓存的ViewModelStore。

    • 2)、onCleared方法在什么调用

    ComponentActivity的构造方法

    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) {
                    if (!isChangingConfigurations()) {
                        //清楚 ViewModel
                        getViewModelStore().clear();
                    }
                }
            }
        });
    }
    

    在ComponentActivity的构造方法中,我们看到,在 Activity 的生命周期为 onDestory的时候,并且当前不是,配置更改(比如横竖屏幕切换)就会调用ViewModelStore 的 clear 方法,进一步回调用 ViewModel 的onCleared方法。

    • 3)、为什么在对应的作用域内,保正只生产出对应的唯一实例

    ViewModelStore的 get方法,是根据key 来取值的,如果 key相同,那取出来的ViewModel就是一个。

    ViewModelStore的源码

    public class ViewModelStore {
         //声明一个 Map 来存储ViewModel
        private final HashMap<String, ViewModel> mMap = new HashMap<>();
         //存储ViewModel
        final void put(String key, ViewModel viewModel) {
            ViewModel oldViewModel = mMap.put(key, viewModel);
            if (oldViewModel != null) {
                oldViewModel.onCleared();
            }
        }
        //取出 ViewModel
        final ViewModel get(String key) {
            return mMap.get(key);
        }
    
        Set<String> keys() {
            return new HashSet<>(mMap.keySet());
        }
    
        //表明要清空存储的数据,还会调用到ViewModel的 clear 方法,也就是最终会调用带 ViewModel 的onCleared()方法
        public final void clear() {
            for (ViewModel vm : mMap.values()) {
                vm.clear();
            }
            mMap.clear();
        }
    }
    

    注意在从 Map中去 ViewModel 的时候是根据 Key,也就是拼接的那个字符串DEFAULT_KEY + ":" + canonicalName。这也就解释为什么在对应的作用域内,保正只生产出对应的唯一实例。

        private static final String DEFAULT_KEY =
                "androidx.lifecycle.ViewModelProvider.DefaultKey";
    

    五、ViewModel简单使用

    5.1 继承ViewMode,实现自定义ViewModel。

    class MyViewModel : ViewModel() {
        private var users: MutableLiveData<String>? = null
    
        fun getUsers(): LiveData<String>? {
            if (users == null) {
                users = MutableLiveData<String>()
                setName()
            }
            return users
        }
    
        fun setName() {
            users?.value = "onexzgj"
        }
    }
    

    5.2 使用ViewModel

    class ViewModelActivity : AppCompatActivity() {
      lateinit var viewmodel: MyViewModel
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_view_model)
            setTitle("ViewModel的使用")
    
            viewModel =  ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(MyViewModel::class.java)
            viewModel.getUsers()?.observe(this, Observer {
                Log.d("ViewModel", it)
            })
        }
    }
    

    相关文章

      网友评论

          本文标题:ViewModel

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