美文网首页Android Jetpack
Jetpack系列-ViewModel使用和源码分析

Jetpack系列-ViewModel使用和源码分析

作者: 木水Code | 来源:发表于2022-05-14 10:22 被阅读0次

    1 简介和简单使用

    1.1 简介

    ViewModel以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转等配置更改后继续留存,保证数据的安全持久性。

    如果Activity/Fragment销毁或重新创建界面,则存储在其中的任何瞬态界面相关数据都会丢失。对于简单的数据,Activity可以使用onSaveInstanceState()方法从onCreate()中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据。

    另一个问题是,Activity/Fragment经常需要进行可能需要一些时间才能返回的异步调用。Activity/Fragment需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。

    ViewModelActiivity重建时会自动保存,可以确保数据不会丢失,并且ViewModel的生命周期比较长,ViewModel对象存在的时间范围是获取ViewModel时传递给ViewModelProviderLifecycleViewModel将一直留在内存中,直到限定其存在时间范围的Lifecycle永久消失:对于Activity,是在Activity完成时;而对于Fragment,是在Fragment分离时。

    ViewModel的生命周期:

    Google官方文档:https://developer.android.google.cn/topic/libraries/architecture/viewmodel?hl=zh_cn

    1.2 使用

    创建RandomVideModel来保存数据,继承RandomVideModel

    class RandomVideModel : ViewModel() {
    
        var num: Int = 0
    
    }
    

    Activity中实例化RandomVideModel,并从中获取数据。

    class VideModelActivity : AppCompatActivity() {
    
        private lateinit var randomVideModel: RandomVideModel
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_vide_model)
    
            randomVideModel = ViewModelProvider(
                this,
                ViewModelProvider.NewInstanceFactory()
            ).get(RandomVideModel::class.java)
    
            tv_content.text = "${randomVideModel.num}"
    
            btn_random.setOnClickListener {
                tv_content.text = "${++randomVideModel.num}"
            }
        }
    }
    

    2 源码分析

    首先看下ViewModel的源码,ViewModel代码很简单,内部用一个HashMap存储数据。

    public abstract class ViewModel {
        // Can't use ConcurrentHashMap, because it can lose values on old apis (see b/37042460)
        @Nullable
        private final Map<String, Object> mBagOfTags = new HashMap<>();
        ...
    }
    

    ViewModel有3个子类,AndroidViewModelFragmentManagerViewModelLoaderViewModel

    AndroidViewModel内部持有一个Application,可以方便使用上下文。

    可以看到,是业务层ActivityViewModel不是直接new出来的,而是通过实例化一个ViewModelProvider对象,然后调用ViewModelProvider的get方法,传入了ViewModel的class文件,所以,ViewModel是通过反射创建出来的。

    ViewModelProvider的构造方法,传入两个参数,一个是ViewModelStoreOwner,一个是FactoryAppCompatActivity继承的是ComponentActivityComponentActivity实现了ViewModelStoreOwner接口,所以这里传this就可以。另一个参数传Factory的实现类NewInstanceFactory

    public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
        owner.viewModelStore,
        factory
    )
    

    接着通过this调用了构造方法ViewModelProvider(ViewModelStore,Factory)ViewModelStore就是存储ViewModel的类,里边是一个HashMap,提供了putgetclear方法。

    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());
        }
    
        //遍历HashMap,清除所有数据
        public final void clear() {
            for (ViewModel vm : mMap.values()) {
                vm.clear();
            }
            mMap.clear();
        }
    }
    

    回到业务层Activity中,调用ViewModelProviderget方法传入了自定义ViewModel的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)
    }
    

    接着调用get(key: String, modelClass: Class<T>)。在get方法中,创建了ViewModel对象,然后将ViewModel存放到ViewModelStore中。

    @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 {
            //由于传进来的是NewInstanceFactory,会走到这里。
            factory.create(modelClass)
        }
        //将ViewModel存放到ViewModelStore中。
        store.put(key, viewModel)
        return viewModel
    }
    

    由于传入的是NewInstanceFactory,所以最终调用的是Factory接口中的create(modelClass: Class<T>)

    public interface Factory {
        public fun <T : ViewModel> create(modelClass: Class<T>): T
    }
    

    而具体实现在NewInstanceFactory中,NewInstanceFactory中的create方法中最终通过反射创建了ViewModel对象。

    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        //noinspection TryWithIdenticalCatches
        try {
            //通过反射创建ViewModel对象。
            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);
        }
    }
    

    来到ComponentActivity中,上面说过,ComponentActivity实现了ViewModelStoreOwner接口。

    public interface ViewModelStoreOwner {
        /**
        * Returns owned {@link ViewModelStore}
        *
        * @return a {@code ViewModelStore}
        */
        @NonNull
        ViewModelStore getViewModelStore();
    }
    

    ViewModelStoreOwner中有一个接口方法getViewModelStoreComponentActivity中实现了该方法。

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

    调用了ensureViewModelStore去拿到ViewModelStore

    void ensureViewModelStore() {
        if (mViewModelStore == null) {
            //这里的NonConfigurationInstances是ComponentActivity中的静态内部类。
            NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }
    

    ensureViewModelStore方法中实例化了一个NonConfigurationInstances,调用了getLastNonConfigurationInstance,从NonConfigurationInstances中拿ViewModelStore

    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
            ? mLastNonConfigurationInstances.activity : null;
    }
    

    而这个方法中又调用了mLastNonConfigurationInstances.activity,点进去跳转到了Activity类中的静态内部类NonConfigurationInstances

    static final class NonConfigurationInstances {
        Object activity;
        HashMap<String, Object> children;
        FragmentManagerNonConfig fragments;
        ArrayMap<String, LoaderManager> loaders;
        VoiceInteractor voiceInteractor;
    }
    

    这里的activityretainNonConfigurationInstances方法中被赋值。

    NonConfigurationInstances retainNonConfigurationInstances() {
        //调用onRetainNonConfigurationInstance获取activity
        Object activity = onRetainNonConfigurationInstance();
        ...
        NonConfigurationInstances nci = new NonConfigurationInstances();
        //赋值给NonConfigurationInstances中的activity
        nci.activity = activity;
        ...
        return nci;
    }
    

    接着来到了Activity类中的onRetainNonConfigurationInstance,该方法的具体实现在ComponentActivity类中。

    Activity中的onRetainNonConfigurationInstance

    public Object onRetainNonConfigurationInstance() {
        return null;
    }
    

    ComponentActivity中的onRetainNonConfigurationInstance

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

    到这里,业务层Activity就拿到了ViewModelStore,就可以操作ViewModel中的数据了。

    而如何保证数据的稳健性不会丢失,可以看ComponentActivity的构造方法。在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) {
                    // Clear out the available context
                    mContextAwareHelper.clearAvailableContext();
                    // And clear the ViewModelStore
                    //如果配置了
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });
     }
    

    这里对Lifecycle状态进行了监听,当状态处于ON_DESTROY,也就是页面销毁时,先判断isChangingConfigurations是否为true,为true的话,就不用调用getViewModelStore().clear(),这样就保证了ViewModel中数据的持久稳健性。

    public boolean isChangingConfigurations() {
        return mChangingConfigurations;
    }
    

    mChangingConfigurations的值,默认是false,但是在因为Configuration的改变被销毁了又重建的时候(最常见的就是横竖屏切换),mChangingConfigurations的值为true,ViewModelStore不调用clear,数据不会清除。这就保证了Activity销毁重建的时候数据不会丢失,还可以继续正常使用。

    /** true if the activity is being destroyed in order to recreate it with a new configuration */
    /*package*/ boolean mChangingConfigurations = false;
    

    4 流程图

    关注木水小站 (zhangmushui.cn)和微信公众号【木水Code】,及时获取更多最新技术干货。

    相关文章

      网友评论

        本文标题:Jetpack系列-ViewModel使用和源码分析

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