美文网首页
Android Jetpack系列——ViewModel源码分析

Android Jetpack系列——ViewModel源码分析

作者: 谭嘉俊 | 来源:发表于2019-12-16 02:32 被阅读0次

    本文章已授权微信公众号郭霖(guolin_blog)转载。

    本文章主要是对ViewModel进行源码分析,建议对着示例代码阅读文章,示例代码如下:

    ViewModelDemo

    本文章使用的是Android SDK 29的源码分析。

    定义

    Android框架管理UI控制器的生命周期(例如:ActivityFragment),Framework可能决定销毁或者重新创建一个UI控制器,以响应某些用户操作或者设备事件,这些操作或者事件完全超出你的控制。

    如果系统销毁或者重新创建一个UI控制器,那么你存储在其中的任何与UI相关的临时数据丢失,例如:你的应用程序在某个Activity中包含一个用户列表,当配置信息更改重新创建Activity时,新的Activity必须重新获取用户列表。对于简单数据,Activity可以使用onSaveInstanceState()方法,并且在onCreate()方法中从Bundle中恢复数据,但是这种方法只适用于少量的、可以序列化和反序列化的数据,而不是潜在的大量数据的用户列表或者是很多的Bitmap

    另外一个问题是UI控制器经常需要进行异步调用,这可能需要一些时间才能返回,UI控制器需要管理这些调用,并确保系统在销毁后对其进行清理,以避免潜在的内存泄露,这种管理需要大量的维护,并且为了配置更改重新创建对象的情况下,这是对资源的浪费,因为对象可能不得不重新发出它已经发出的调用。

    UI控制器(例如:ActivityFragment)主要用于显示UI数据响应用户操作或者处理操作系统通信(例如:权限请求),要求UI控制器也负责从数据库或者网络加载数据会使类膨胀,将过多的责任分配给UI控制器会导致单个类视图自己处理应用程序的所有工作,而不是将工作委托给其他类,这样也会使测试变得更加困难。

    视图数据所有权UI控制器的逻辑中分离出来会更加简单、更有效,所以官方推出这样一个组件:ViewModel

    ViewModel是一个负责准备和管理Activity或者Fragment的类,它还可以处理ActivityFragment与应用程序其余部分的通信(例如:调用业务逻辑类)。

    ViewModel总是在一个Activity或者一个Fragment创建的,并且只要对应的Activity或者Fragment处于活动状态的话,它就会被保留(例如:如果它是个Activity,就会直到它finished)。

    换句话说,这意味着一个ViewModel不会因为配置的更改(例如:旋转)而被销毁,所有的新实例将被重新连接到现有的ViewModel

    ViewModel的目的是获取保存Activity或者Fragment所需的信息,Activity或者Fragment应该能够观察到ViewModel中的变化,通常通过LiveData或者Android Data Binding公开这些信息。

    注意的是,ViewModel的唯一职责是管理UI的数据,它不应该访问你的视图层次结构或者保留对Activity或者Fragment的引用

    以下这张图片表示Activity经历屏幕旋转而后结束的过程中所处的各种生命周期状态,还在关联的Activity生命周期的旁边显示了ViewModel的生命周期:

    ViewModelLifecycle.png

    示例代码

    项目加上如下依赖:

    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-alpha02'
    

    由于我这边用到了DataBinding,所以加上如下代码:

    dataBinding {
        enabled = true
    }
    

    项目结构如下图:

    ViewModelDemoProjectStructureDiagram.png

    我这边定义了一个继承了ViewModel,并且实现了ObservableObservableViewModel类,来通知控件数据的变化,也可以使用LiveData来实现这样的功能,代码如下:

    package com.tanjiajun.viewmodeldemo.viewmodel
    
    import androidx.databinding.Observable
    import androidx.databinding.PropertyChangeRegistry
    import androidx.lifecycle.ViewModel
    
    /**
     * Created by TanJiaJun on 2019-11-24.
     */
    open class ObservableViewModel : ViewModel(), Observable {
    
        private val callbacks = PropertyChangeRegistry()
    
        override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) =
            callbacks.add(callback)
    
        override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) =
            callbacks.remove(callback)
    
        fun notifyChange() =
            callbacks.notifyCallbacks(this, 0, null)
    
        fun notifyPropertyChanged(fieldId: Int) =
            callbacks.notifyCallbacks(this, fieldId, null)
    
    }
    

    第一个例子:ViewModel不会因为配置更改而被销毁

    MainActivity中创建MainViewModel,代码如下:

    // MainActivity.kt
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main).also {
            it.viewModel = ViewModelProviders.of(this)[MainViewModel::class.java].apply {
                name = "谭嘉俊"
                age = 25
                gender = "男"
            }
            it.handlers = this
        }
    }
    

    这个Activity所对应的界面可以跟随手机屏幕旋转,而且没有通过android:configChanges指定属性,让Activity在指定属性变化的时候,只会调用ActivityonConfigurationChanged()方法,而不会被销毁重建,代码如下:

    android:configChanges="orientation|screenSize|keyboardHidden"
    

    当我们旋转手机屏幕的时候,发现这个Activity的内容没有发生变化,符合我们的预期。

    第二个例子是:Fragment使用Activity共享的ViewModel处理数据

    定义NameViewModel,并且继承我在上面说的ObservableViewModel,代码如下:

    package com.tanjiajun.viewmodeldemo.viewmodel
    
    import androidx.databinding.Bindable
    import androidx.databinding.library.baseAdapters.BR
    
    /**
     * Created by TanJiaJun on 2019-11-24.
     */
    class NameViewModel : ObservableViewModel() {
    
        @get:Bindable
        var name = ""
            set(value) {
                field = value
                notifyPropertyChanged(BR.name)
            }
    
    }
    

    FirstNameFragment使用一个和NameActivity生命周期相同的NameViewModel,代码如下:

    // FirstNameFragment.kt
    private var viewModel: NameViewModel? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // retainInstance方法下面会解析
        retainInstance = true
        viewModel = activity?.let {
            ViewModelProviders.of(it)[NameViewModel::class.java].apply {
                name = "谭嘉俊"
            }
        }
    }
    
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? =
        DataBindingUtil.inflate<FragmentFirstNameBinding>(
            inflater,
            R.layout.fragment_first_name,
            container,
            false
        )
            .also {
                // 使用NameActivity共享的NameViewModel
                it.viewModel = viewModel
                it.handlers = this
            }
            .root
    

    retainInstance方法控制在Activity重新创建(例如:配置更改)期间是否重新创建Fragment实例,如果设为true的话,在Activity重新创建的时候,Fragment的生命周期会有点不一样,onCreate(Bundle)方法将不会被调用,因为Fragment没有重新创建,onDestroy()不会被调用,但是onDetach()方法会被调用,因为Fragment只是从它附加的Activity分离而已,onAttach(Activity)方法和onActivityCreated(Bundle)方法仍然会被调用。

    // SecondNameFragment.kt
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? =
        DataBindingUtil.inflate<FragmentSecondNameBinding>(
            inflater,
            R.layout.fragment_second_name,
            container,
            false
        )
            .also { it.handlers = this }
            .root
    
    // 点击按钮后会改变NameViewModel中name的属性值
    override fun onChangeNameToAppleClick(view: View) {
        activity?.let { ViewModelProviders.of(it)[NameViewModel::class.java].name = "苹果" }
    }
    

    点击SecondFragment的按钮后,我们再按后退键,退回到上个Fragment,可以看到name已经已经从”谭嘉俊“变成”苹果“了,这里的NameViewModel的生命周期是和NameActivity的生命周期一样,也就是这两个Fragment拿到的都是同一个ViewModel,所以我们可以这样处理附加在同一个Activity多个Fragment之间的数据。

    源码分析

    我们看下ViewModelProviders这个类,代码如下:

    // 创建一个ViewModelProvider,在Fragment处于活动状态时保留ViewModel
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment) {
        return of(fragment, null);
    }
    
    // 创建一个ViewModelProvider,在Activity处于活动状态时保留ViewModel
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        return of(activity, null);
    }
    
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
        // 检查Fragment是否附加在Application
        Application application = checkApplication(checkActivity(fragment));
        // 在上面的方法中factory是传null
        if (factory == null) {
            // 创建一个单例的AndroidViewModelFactory
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        // 创建ViewModelProvider,这里会拿到Fragment的ViewModelStore,下面会分析
        return new ViewModelProvider(fragment.getViewModelStore(), factory);
    }
    
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        // 检查Activity是否附加在Application
        Application application = checkApplication(activity);
        // 在上面的方法中factory是传null
        if (factory == null) {
            // 创建一个单例的AndroidViewModelFactory
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        // 创建ViewModelProvider,这里会拿到Activity的ViewModelStore,下面会分析
        return new ViewModelProvider(activity.getViewModelStore(), factory);
    }
    

    我们看下ActivitygetViewModelStore()方法,代码如下:

    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        // 检查Activity是否附加在Application
        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) {
            // 通过getLastNonConfigurationInstance()方法得到NonConfigurationInstances,下面会分析
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // 从NonConfigurationInstances恢复ViewModelStore
                mViewModelStore = nc.viewModelStore;
            }
            // 如果是空的话创建ViewModelStore对象
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
    

    在分析getLastNonConfigurationInstances()方法之前,我们看下onRetainNonConfigurationInstance()方法,它在Activity类中是一个返回null的方法,我们可以找到Activity的子类ComponentActivity,可以看到它重写了这个方法,代码如下:

    // ComponentActivity.java
    // NonConfigurationInstances是一个final的静态类,里面有两个变量:custom和viewModelStore
    static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }
    
    @Override
    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        // onRetainCustomNonConfigurationInstance()方法已弃用
        Object custom = onRetainCustomNonConfigurationInstance();
    
        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // 如果viewModelStore是null的话,证明没人调用getViewModelStore(),所以看看我们最后一个NonConfigurationInstance是否存在ViewModelStore
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // 如果有的话,就从NonConfigurationInstances取出ViewModelStore
                viewModelStore = nc.viewModelStore;
            }
        }
    
        if (viewModelStore == null && custom == null) {
            // 如果ViewModelStore还是null而且custom也是null的话,证明没有NonConfigurationInstances
            return null;
        }
    
        // 如果有ViewModelStore或者有custom的话,就创建NonConfigurationInstances对象,并且对其进行赋值
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }
    

    onRetainNonConfigurationInstance()方法是在一个Activity因为配置改变被销毁时被调用,这时就会创建一个新的实例,它会在onStop()方法和onDestroy()方法两者之间调用,我们可以在这里返回对象,甚至是Activity的实例也可以,之后我们可以在新的Activity的实例通过getLastNonConfigurationInstance()方法来检索,拿到我们想要的对象。

    我们之前也用过onSaveInstanceState方法,调用这个方法可以在Activity被终止之前检索每个实例的状态,以便可以在onCreate方法或者onRestoreInstanceState方法中恢复状态,两个方法都会传入我们之前想要保留的Bundle对象,注意你还要给你的View设置id,因为它是通过id保存当前有焦点View,在Android P版本中,这个方法将在onStop()方法之后调用,在之前的版本中,这个方法将在onStop()方法之前调用,并不能保证它将在onPause()方法之前或之后调用,这个方法默认实现保存了关于Activity的视图层次状态的临时信息,例如:EditText中的文本和ListView或者RecyclerView中的滚动条位置。

    onSaveInstanceState方法和onRetainNonConfigurationInstance()方法还有什么区别呢?其中一点是前者是保存到BundleBundle是有类型限制大小限制的,而且也要在主线程序列化和反序列化数据,而后者是保存到Object类型和大小都没有限制

    我们继续看源码,从上面分析可知,会创建ViewModelStore对象,我们看下ViewModelStore的源码,代码如下:

    public class ViewModelStore {
    
        // 创建一个key为String,value为ViewModel的HashMap对象
        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中的所有的ViewModel不再被使用
        public final void clear() {
            for (ViewModel vm : mMap.values()) {
                vm.clear();
            }
            mMap.clear();
        }
    }
    

    它是通过HashMap存放ViewModel的,然后我们回到上面ViewModelProvidersof方法,可以看到它创建了ViewModelProvider对象,看下它的构造方法,代码如下:

    // ViewModelProvider.java
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }
    

    构造方法就两个参数,第一个是ViewModelStore,用于存放ViewModel;第二个参数是Factory,用于实例化多个ViewModel工厂

    在我们的示例代码中,我们调用了ViewModelProviderget方法,传入的是我们创建的ViewModelClass对象,看下相关的代码,代码如下:

    // ViewModelProvider.java
    private static final String DEFAULT_KEY =
            "androidx.lifecycle.ViewModelProvider.DefaultKey";
    
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        // 得到ViewModel的Class对象的Java语言规范定义的底层类的规范名称
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        // 调用下面的get方法,传入“DEFAULT_KEY:modelClass的规范名称”字符串和ViewModel的Class对象
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
    
    @SuppressWarnings("unchecked")
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        // 通过key从ViewModelStore中的HashMap中得到ViewModel
        ViewModel viewModel = mViewModelStore.get(key);
    
        // 判断从ViewModelStore中得到的ViewModel是否是Class对象的一个实例,也就是说判断ViewModelStore中是否存在我们想要的ViewModel
        if (modelClass.isInstance(viewModel)) {
            // 如果有的话就返回对应的ViewModel
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        // 如果没有的话就创建ViewModel
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            // 根据上面的代码可知,mFactory是Factory的实现类NewInstanceFactory的子类AndroidViewModelFactory,所以我们调用的是这段逻辑
            viewModel = (mFactory).create(modelClass);
        }
        // 将创建好的ViewModel存放到ViewModelStore
        mViewModelStore.put(key, viewModel);
        // 返回ViewModel
        return (T) viewModel;
    }
    

    我们看下AndroidViewModelFactorycreate方法,代码如下:

    // ViewModelProvider中的静态内部类AndroidViewModelFactory
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        // 判断AndroidViewModel所表示的类或接口是否与modelClass所表示的类或接口相同,或者是否为其超类或超接口
        if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
            //noinspection TryWithIdenticalCatches
            try {
                // 创建ViewModel对象
                return modelClass.getConstructor(Application.class).newInstance(mApplication);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
        // 根据我们的示例代码,我们传入的modelClass不是AndroidViewModel,而且也不是为其超类或者超接口,所以会执行以下逻辑
        return super.create(modelClass);
    }
    

    ViewModel是不能传入任何有Context引用的对象,这样导致内存泄露,如果需要使用的话,可以使用AndroidViewModel

    然后会调用它的父类NewInstanceFactorycreate方法,代码如下:

    @SuppressWarnings("ClassNewInstance")
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        //noinspection TryWithIdenticalCatches
        try {
            // 创建由modelClass类对象表示的类的新实例
            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);
        }
    }
    

    刚才我们看的是ActivitygetViewModelStore()方法,现在看下FragmentgetViewModelStore()方法,代码如下:

    // Fragment.java
    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        // 判断Fragment是否已经与Activity分离
        if (mFragmentManager == null) {
            throw new IllegalStateException("Can't access ViewModels from detached fragment");
        }
        return mFragmentManager.getViewModelStore(this);
    }
    

    调用了FragmentManagerImplgetViewModelStore方法,代码如下:

    // FragmentManagerImpl.java
    @NonNull
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        return mNonConfig.getViewModelStore(f);
    }
    

    成员变量mNonConfigFragmentManagerVIewModel的引用,我们看下FragmentManagerViewModelgetViewModelStore方法,代码如下:

    // FragmentManagerViewModel.java
    @NonNull
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
        if (viewModelStore == null) {
            viewModelStore = new ViewModelStore();
            mViewModelStores.put(f.mWho, viewModelStore);
        }
        return viewModelStore;
    }
    

    成员变量mViewModelStoreskeyStringvalueViewModelStoreHashMap的引用,它是跟随Fragment的生命周期,根据Frament的内部唯一名称从这个HashMap中得到ViewModelStore,如果是空的话,就创建一个新的ViewModelStore对象,并且放入mViewModelStores,然后返回这个对象;如果不是空的话,就返回刚才从HashMap取得的ViewModelStore

    到这里,ViewModel创建得到的源码就分析得差不多了,然后我们看下ViewModel什么时候被销毁,在上面分析ViewModelStore源码的时候,我们看到有个clear方法,这个方法用来清除内部存储并且通知存储在这个HashMap中的所有的ViewModel不再被使用,如果ViewModel跟随的Activity的生命周期的话,它会在如下代码调用这个方法:

    public ComponentActivity() {
        // 省略部分代码
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                // 判断是否接收到Activity的destroy状态
                if (event == Lifecycle.Event.ON_DESTROY) {
                    // 如果接收到,判断是否因为配置更改导致的destroy
                    if (!isChangingConfigurations()) {
                        // 如果不是,调用ViewModelStore的clear方法
                        getViewModelStore().clear();
                    }
                }
            }
        });
        // 省略部分代码
    }
    

    如果ViewModel跟随的是Fragment的生命周期的话,它会在如下代码调用这个方法:

    // FragmentManagerViewModel.java
    void clearNonConfigState(@NonNull Fragment f) {
        if (FragmentManagerImpl.DEBUG) {
            Log.d(FragmentManagerImpl.TAG, "Clearing non-config state for " + f);
        }
        // 清除并且删除Fragment的子配置状态
        FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);
        if (childNonConfig != null) {
            childNonConfig.onCleared();
            mChildNonConfigs.remove(f.mWho);
        }
        // 清除并且删除Fragment的ViewModelStore
        ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
        if (viewModelStore != null) {
            viewModelStore.clear();
            mViewModelStores.remove(f.mWho);
        }
    }
    

    在如下代码调用clearNonConfigState这个方法:

    // FragmentManagerImpl.java
    @SuppressWarnings("ReferenceEquality")
    void moveToState(Fragment f, int newState, int transit, int transitionStyle,
                     boolean keepActive) {
        // 省略部分代码
        if (f.mState <= newState) {
            // 省略部分代码
        } else if (f.mState > newState) {
            switch (f.mState) {
                // 省略部分代码
                case Fragment.CREATED:
                    if (newState < Fragment.CREATED) {
                        // 省略部分代码
                        if (f.getAnimatingAway() != null || f.getAnimator() != null) {
                            // 省略部分代码
                        } else {
                            if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
                            boolean beingRemoved = f.mRemoving && !f.isInBackStack();
                            // 判断Fragment是否正在remove,同时还没放入后退栈,或者判断是否FragmentManagerViewModel是否应该销毁
                            if (beingRemoved || mNonConfig.shouldDestroy(f)) {
                                boolean shouldClear;
                                // 判断mHost是否是ViewModelStoreOwner的实例
                                if (mHost instanceof ViewModelStoreOwner) {
                                    // 如果是,shouldClear的值就是FragmentManagerViewModel是否已经清除
                                    shouldClear = mNonConfig.isCleared();
                                } else if (mHost.getContext() instanceof Activity) {
                                    Activity activity = (Activity) mHost.getContext();
                                    shouldClear = !activity.isChangingConfigurations();
                                } else {
                                    shouldClear = true;
                                }
                                // 根据beingRemoved或者shouldClear的值来判断是否需要清除ViewModel
                                if (beingRemoved || shouldClear) {
                                    // 如果是,调用clearNonConfigState方法
                                    mNonConfig.clearNonConfigState(f);
                                }
                                // 执行Fragment的onDestroy()方法
                                f.performDestroy();
                                dispatchOnFragmentDestroyed(f, false);
                            } else {
                                f.mState = Fragment.INITIALIZING;
                            }
                            // 省略部分代码
                        }
                    }
            }
        }
    
        // 省略部分代码
    }
    

    到这里,ViewModel销毁的源码分析得差不多了。

    onSaveInstanceState和ViewModel

    onSaveInstanceState是生命周期的一个回调方法,用来保存以下两种状态下的少量UI相关的数据:

    • 应用的进程在后台的时候由于内存限制而被终止。
    • 配置更改。

    onSaveInstanceState不是被设计用来储存类似Bitmap这样大的数据,而是储存小的与UI相关的能够被序列化和反序列化的数据,上面也提及过了,这里就不再赘述了。

    ViewModel有以下好处:

    • ViewModel可以架构设计更加良好,UI代码数据分离,使代码更加遵循单一职责原则更加模块化更易于测试
    • ViewModel能储存更大更复杂的数据,而且数据类型也没有限制,甚至可以储存Activity实例

    要注意的是,ViewModel只能在配置更改造成相关的销毁下得到保留,而不能在被终止的进程中得到保留,也就是说在应用的进程在后台的时候由于内存限制而被终止,ViewModel也会被销毁

    因此我们最好两者结合来处理保存和恢复UI状态,如果要保证数据不丢失,就要对数据进行本地持久化

    题外话

    如果应用在特定配置更改期间无需更新资源,并且因性能限制你需要避免Activity重启,则可声明Activity自行处理配置更改,从而阻止系统重启Activity

    我们可以通过在AndroidManifest文件中,找到相应<activity>元素,添加android:configChanges属性,在声明多个配置值的时候,可以通过|字符对其进行分隔。

    Android官方不建议对大多数应用使用此方法,因为这样做可能会提高使用备用资源的难度。

    有如下属性:

    • density:显示密度发生变更,例如:用户可能已指定不同的显示比例,或者有不同的显示现处于活跃状态。请注意:此项为 API 级别 24 中的新增配置。

    • fontScale:字体缩放系数发生变更,例如:用户已选择新的全局字号。

    • keyboard:键盘类型发生变更,例如:用户插入外置键盘。

    • keyboardHidden:键盘无障碍功能发生变更,例如:用户显示硬键盘。

    • layoutDirection:布局方向发生变更,例如:自从左至右 (LTR) 更改为从右至左 (RTL)。请注意:此项为 API 级别 17 中的新增配置。

    • locale:语言区域发生变更,例如:用户已为文本选择新的显示语言。

    • mcc:IMSI 移动设备国家/地区代码 (MCC) 发生变更,例如:检测到 SIM 并更新 MCC。

    • mnc:IMSI 移动设备网络代码 (MNC) 发生变更,例如:检测到 SIM 并更新 MNC。

    • navigation:导航类型(轨迹球/方向键)发生变更。(这种情况通常不会发生。)

    • orientation:屏幕方向发生变更,例如:用户旋转设备。请注意:如果应用面向 Android 3.2(API 级别 13)或更高版本的系统,则还应声明screenSize配置,因为当设备在横向与纵向之间切换时,该配置也会发生变更。

    • screenLayout:屏幕布局发生变更,例如:不同的显示现可能处于活跃状态。

    • screenSize:当前可用屏幕尺寸发生变更。该值表示当前可用尺寸相对于当前纵横比的变更,当用户在横向与纵向之间切换时,它便会发生变更。请注意:此项为 API 级别 13 中的新增配置。

    • smallestScreenSize:物理屏幕尺寸发生变更。该值表示与方向无关的尺寸变更,因此它只有在实际物理屏幕尺寸发生变更(如切换到外部显示器)时才会变化。对此配置所作变更对应smallestWidth配置的变化。请注意:此项为 API 级别 13 中的新增配置。

    • touchscreen:触摸屏发生变更。(这种情况通常不会发生。)

    • uiMode:界面模式发生变更,例如:用户已将设备置于桌面或车载基座,或者夜间模式发生变更。如需了解有关不同界面模式的更多信息,请参阅UiModeManager请注意:此项为 API 级别 8 中的新增配置。

    所有这些配置变更都可能影响到应用所看到资源值,因此调用onConfigurationChanged()方法时,通常有必要再次检索所有资源(包括视图布局、可绘制对象等),以正确处理变更。

    我的GitHub:TanJiaJunBeyond

    Android通用框架:Android通用框架

    我的掘金:谭嘉俊

    我的简书:谭嘉俊

    我的CSDN:谭嘉俊

    相关文章

      网友评论

          本文标题:Android Jetpack系列——ViewModel源码分析

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