美文网首页
从面试角度看ViewModel

从面试角度看ViewModel

作者: 艾伦宇 | 来源:发表于2021-11-19 11:09 被阅读0次

    一、问题

    1. 为什么要用ViewModel,有什么好处
    2. ViewModel如何在配置变化时保留数据
    3. 在界面旋转时,ViewModel中的请求还在执行吗
    4. ViewModel如何做到在页面销毁时,停止协程中的工作

    二、解答

    问题1、为什么要用ViewModel,有什么好处

    1. 数据状态的维护
      1. 旋转后保证数据还在
      2. 跟随Activity生命周期的作用域,防止内存泄漏
    2. 便于单元测试
      1. 只用于做纯的业务代码
      2. 不持有Activity或View对象
    3. 便于共享
      同一个Activity下的不同Fragment之间共享

    问题2、ViewModel如何在配置变化时保留数据

    利用的是Activity重建机制中的NonConfigurationInstances来保存
    也就是在配置变化时,Activity会被销毁,但是重建时会从旧的Activity中拿到ViewModelStore

    问题3、在界面旋转时,ViewModel中的请求还在执行吗

    还会在,并不会调用ViewModel的clear方法
    因为系统会判断是否因为页面在旋转导致的,如果是则不销毁ViewModel

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

    问题4、ViewModel如何做到在页面销毁时,停止协程中的工作

    这是因为ViewModel会去监听Activity/Fragment的生命周期
    在onDestory时,且不需要重建的情况下,会去调用ViewModel的clear方法
    在这个方法里面会去close掉所有支持Closeable的对象
    如 viewModelScope

    //viewModelScope
    public val ViewModel.viewModelScope: CoroutineScope
        get() {
            val scope: CoroutineScope? = this.getTag(JOB_KEY)
            if (scope != null) {
                return scope
            }
            return setTagIfAbsent(
                JOB_KEY,
                CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
            )
        }
    internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
        override val coroutineContext: CoroutineContext = context
        override fun close() {
            coroutineContext.cancel()
        }
    }
    
    // ViewModel的clear方法
        @MainThread
        final void clear() {
            mCleared = true;
            // Since clear() is final, this method is still called on mock objects
            // and in those cases, mBagOfTags is null. It'll always be empty though
            // because setTagIfAbsent and getTag are not final so we can skip
            // clearing it
            if (mBagOfTags != null) {
                synchronized (mBagOfTags) {
                    for (Object value : mBagOfTags.values()) {
                        // see comment for the similar call in setTagIfAbsent
                        closeWithRuntimeException(value);
                    }
                }
            }
            onCleared();
        }
        private static void closeWithRuntimeException(Object obj) {
            if (obj instanceof Closeable) {
                try {
                    ((Closeable) obj).close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    

    三、参考

    Android官方架构组件ViewModel:从前世今生到追本溯源

    相关文章

      网友评论

          本文标题:从面试角度看ViewModel

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