Jetpack-ViewModel(面试深度起来)

面试素质三联?

  • ViewModel优点是啥? 答:保存数据 自动管理。

  • Fragment能拿Activity的ViewModel么? 答:能吧?。 = = !

  • ViewModel怎么管理的? 答:母鸡。

  • ViewModel怎么创建的? 答:母鸡啊

    结果

    回去等消息吧,面试官顺手评价 一个深度不够,基础不牢。

逐步拆解

kotlin 复制代码
 private  val  model by lazy {
        ViewModelProvider(this).get(BaseViewModel::class.java);
    }

ViewModelProvider干了啥?

kotlin 复制代码
  public constructor(
        owner: ViewModelStoreOwner
    ) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
​
​
​
//构建Factory 
 internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
                if (owner is HasDefaultViewModelProviderFactory)
                    owner.defaultViewModelProviderFactory else instance
​

整体看了一下,大体就三件事, 用this的 viewModelStore,搞Factory, 搞参数。

this 里面搞大事。

原来ViewModelProvider需要传入一个ViewModelStoreOwner 接口

scss 复制代码
 
public constructor(
        owner: ViewModelStoreOwner
    ) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))

看下ViewModelStoreOwner 就一个方法,返回一个ViewModelStore

kotlin 复制代码
interface ViewModelStoreOwner {
    val viewModelStore: ViewModelStore
}

我们常用的ComponentActivity/Fragment都实现了ViewModelStoreOwnerHasDefaultViewModelProviderFactory ,我们看下他们的实现getViewModelStore()的区别。

ComponentActivity的实现方式

scss 复制代码
   @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.");
        }
        ensureViewModelStore();
        return mViewModelStore;
    }
​
   void ensureViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
​
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }

简略看下核心代码,getLastNonConfigurationInstance没有,就直接new一个返回。

ComponentActivity 还是比较简单的,相当于自己管自己。
*

Fragment的实现(版本 1.6.2)

less 复制代码
 @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
       // 省略部分代码
        return mFragmentManager.getViewModelStore(this);
    }
 
​

Fragment 调用FragmentManagergetViewModelStore(),(下面简称Fm)并把自己传入进去了。

追一下看看 进入 Fm。

less 复制代码
 private FragmentManagerViewModel mNonConfig; 
@NonNull
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        return mNonConfig.getViewModelStore(f);
    }

FragmentManagerViewModel

ini 复制代码
FragmentManagerViewModel extends ViewModel{
//省略部分代码
private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();
  @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;
    }
  
   //省略部分代码
   }
​

FragmentManager 调用自己的mNonConfig,传入了FragmentmNonConfig 自己就是一个FragmentManagerViewModel。也是一个ViewModel

到这里我们理解Frament 其实委托给了FragmentManager

先看下Fm里的mNonConfig 咋来的。看初始化

FragmentManager

less 复制代码
void attachController(@NonNull FragmentHostCallback<?> host,
            @NonNull FragmentContainer container, @Nullable final Fragment parent) {
 //省略部分代码
 
   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);
        }
    }
        
        mNonConfig.setIsStateSaved(isStateSaved());
// 设置setNonConfig()
        mFragmentStore.setNonConfig(mNonConfig);

FragmentManagerViewModel

csharp 复制代码
@NonNull
    static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
        ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
                FACTORY);
        return viewModelProvider.get(FragmentManagerViewModel.class);
    }

我们解析三个创建

  • if 可以理解为fragment嵌套 ,去找getChildNonConfig()
  • else if 取到hostviewModelStore,自己创建了一个 ViewModelProvider 缓存FragmentManagerViewModel类进去。
  • else 自己 host 取不到,自己创建一个FragmentManagerViewModel,传入false,表示不自动缓存。

我们看下看下host的真面目

Fragment

csharp 复制代码
 void performAttach() {
   //省略部分代码
     mChildFragmentManager.attachController(mHost, createFragmentContainer(), this);
    }

host 看下哪里初始化的

FragmentController

less 复制代码
 public void attachHost(@Nullable Fragment parent) {
        mHost.mFragmentManager.attachController(
                mHost, mHost /*container*/, parent);
    }

再看下 这个mHost怎么初始化的

typescript 复制代码
public static FragmentController createController(FragmentHostCallback<?> callbacks) {
        return new FragmentController(callbacks);
    }

追一下 进入到了FragmentActivity

scala 复制代码
 final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

//内部类
 class HostCallbacks extends FragmentHostCallback<FragmentActivity>{
    // 省略部分代码
      @NonNull
        @Override
        public ViewModelStore getViewModelStore() {
            return FragmentActivity.this.getViewModelStore();
        }
 }

捋一下,Fragment里面的 mNonConfig.getViewModelStore(f), 最后会调用到 host .getViewModelStore()。而 host 是 FragmentActivity 里的一个内部类。 最后 调用到 FragmentActivity.this.getViewModelStore()。 而FragmentActivity继承了ComponentActivity

正常情况下来说,FragmentManager 调用的是 ComponentActivitygetViewModelStore(),拿到了 然后 构建了一个ViewModelProvider() 返回。

实在不行了,自己 创建一个 FragmentManagerViewModel(false)用于处理。

ViewModelStore是个啥?

kotlin 复制代码
open class ViewModelStore {

    private val map = mutableMapOf<String, ViewModel>()

  
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    fun put(key: String, viewModel: ViewModel) {
        val oldViewModel = map.put(key, viewModel)
        oldViewModel?.onCleared()
    }
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    operator fun get(key: String): ViewModel? {
        return map[key]
    }

  
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    fun keys(): Set<String> {
        return HashSet(map.keys)
    }

    fun clear() {
        for (vm in map.values) {
            vm.clear()
        }
        map.clear()
    }
}

一眼明白,一个map,能存ViewModel。 能清除,无了。

ViewModelProvider. get() 又搞了啥?

kotlin 复制代码
 @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)
    }

    @Suppress("UNCHECKED_CAST")
    @MainThread
    public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
        val 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.
            }
        }
        val extras = MutableCreationExtras(defaultCreationExtras)
        extras[VIEW_MODEL_KEY] = key

        return try {
            factory.create(modelClass, extras)
        } catch (e: AbstractMethodError) {
            factory.create(modelClass)
        }.also { store.put(key, it) }
    }

扫一眼,拿传入的类名 canonicalName 前面拼接 "DEFAULT_KEY",如果当前store有,且是 是当前类的实例,就强转返回。 没有就factory 创建。最后调用 also缓存进去。看下 factory

前面我们知道ComponentActivityFragment实现了HasDefaultViewModelProviderFactory

  • ComponentActivity
less 复制代码
 @NonNull
    @Override
    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        if (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }
  • Fragment

    ini 复制代码
       @NonNull
        @Override
        public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
            if (mFragmentManager == null) {
                throw new IllegalStateException("Can't access ViewModels from detached fragment");
            }
            if (mDefaultFactory == null) {
                Application application = null;
                Context appContext = requireContext().getApplicationContext();
                while (appContext instanceof ContextWrapper) {
                    if (appContext instanceof Application) {
                        application = (Application) appContext;
                        break;
                    }
                    appContext = ((ContextWrapper) appContext).getBaseContext();
                }
                if (application == null && FragmentManager.isLoggingEnabled(Log.DEBUG)) {
                    Log.d(FragmentManager.TAG, "Could not find Application instance from "
                            + "Context " + requireContext().getApplicationContext() + ", you will "
                            + "need CreationExtras to use AndroidViewModel with the default "
                            + "ViewModelProvider.Factory");
                }
                mDefaultFactory = new SavedStateViewModelFactory(
                        application,
                        this,
                        getArguments());
            }
            return mDefaultFactory;
        }

很好他们都返回了SavedStateViewModelFactory()。看下SavedStateViewModelFactorycreat();

  • SavedStateViewModelFactory.create()
less 复制代码
  @NonNull
    @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);
    }


 public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
   //判断是否是isAndroidViewModel
        boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
        Constructor<T> constructor;
        if (isAndroidViewModel) {
            constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
        } else {
            constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
        }
        // doesn't need SavedStateHandle
        if (constructor == null) {
            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,再往下一看newInstance(),反射, 绝对的反射。

到此为止,我们知道 get() 其实就是缓存则取,没缓存就反射搞个对象 ,also 缓存进去。

至此,还没成艺术。

ViewModel 的自我修养(管理)?

认真阅读的朋友们都知道,Fragment正常情况下用FragmentActivityViewModelStore

  • 看下FragmentActivity 的 父类ComponentActivity 构造参数。
less 复制代码
public ComponentActivity() {

  //省略部分代码
getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                
                    mContextAwareHelper.clearAvailableContext();
                 
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });

 


}

我们看到 做了个监听,在 *ON_DESTROY*的时候, 判断了下!isChangingConfigurations()也就是 不是发生了配置变化,是真正销毁的时候,调用了 getViewModelStore().clear()

其实就是相当于把当前缓存的ViewModel 的对象嘎嘎的清空了。

经常说ViewModel 为什么不建议持有Context。 因为是在onDestroy后才执行。

也为什么说旋转屏幕ViewModel 不会丢数据。因为虽然走了onDestroy 但是内部判断了是否旋转屏幕。

  • Fragment 怎么管理的呢

    我们记得Fragment 在 this 那一步的时候,有2种 情况,一种 拿FragmentActivity的ViewModelStore 。一种自己构建了一个FragmentManagerViewModel();

    这个mNonConfig 其实在创建后 赛进了mFragmentStore

    ini 复制代码
     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);
            }
            // Ensure that the state is in sync with FragmentManager
            mNonConfig.setIsStateSaved(isStateSaved());
            mFragmentStore.setNonConfig(mNonConfig);

    mFragmentStore.setNonConfig(mNonConfig);mNonConfig传出去了,追着看一下。

    进入 FragmentStore

    发现 FragmentStore 是在FragmentManager初始化就创建了

    java 复制代码
     private final FragmentStore mFragmentStore = new FragmentStore();

    FragmentStore里调用mNonConfig 查看下被调用的地方。

    csharp 复制代码
     FragmentManagerViewModel getNonConfig() {
            return mNonConfig;
        }

    进入FragmentManager.java

    ->clearBackStackStateViewModels()

    这里去判断了哪些Fragment 需要清理

    ini 复制代码
    private void clearBackStackStateViewModels() {
            boolean shouldClear;
            if (mHost instanceof ViewModelStoreOwner) {
                shouldClear = mFragmentStore.getNonConfig().isCleared();
            } else if (mHost.getContext() instanceof Activity) {
                Activity activity = (Activity) mHost.getContext();
                shouldClear = !activity.isChangingConfigurations();
            } else {
                shouldClear = true;
            }
            if (shouldClear) {
                for (BackStackState backStackState : mBackStackStates.values()) {
                    for (String who : backStackState.mFragments) {
                       mFragmentStore.getNonConfig().clearNonConfigState(who, false);
                    }
                }
            }
        }

    ->dispatchDestroy()

    scss 复制代码
     void dispatchDestroy() {
            mDestroyed = true;
            execPendingActions(true);
            endAnimatingAwayFragments();
            clearBackStackStateViewModels();
            //省略代码
            }

    -> FragmentController.java->dispatchDestroy()

    csharp 复制代码
    public void dispatchDestroyView() {
            mHost.mFragmentManager.dispatchDestroyView();
        }

    -> FragmentActivity.java->onDestroy(){}

    scss 复制代码
     @Override
        protected void onDestroy() {
            super.onDestroy();
            mFragments.dispatchDestroy();
           mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
        }

    把整个调用反过来看一下,FragmentActivityonDestroy 调用

    mFragments.dispatchDestroy(); (FragmentController.java)

    -> FragmentManager.dispatchDestroy() (FragmentManger.java)

    -> clearBackStackStateViewModels()

    可以理解为FragmentActivity销毁才销毁。

    另外 FragmentStateManager 里 重新构建的时候,也会销毁清理对应的ViewModel。感兴趣可以看一下。

    scss 复制代码
    void moveToExpectedState{
    //省列部分代码
    case Fragment.ATTACHED:
    if (mFragment.mBeingSaved
      && mFragmentStore.getSavedState(mFragment.mWho) == null) {                              mFragmentStore.setSavedState(mFragment.mWho, saveState());
                                }
                                destroy(); 
                                }
     void destroy() {
       //省列部分代码
        if (shouldDestroy) {
           mFragment.performDestroy();
            if ((beingRemoved && !mFragment.mBeingSaved) || shouldClear) {            mFragmentStore.getNonConfig().clearNonConfigState(mFragment, false);
                }
                mFragment.performDestroy();
        }
     }

面试的侃侃而谈

  • 优势

    • 保存数据,页面变化能缓存
    • 自动管理,页面销毁自动清理
    • FragmentActivity可共用。
  • Fragment能拿 ActivityViewModel么?

    • 能 ,毕竟那玩意只看传入的类名,把Activity 一传就行。
  • 怎么自动管理的?

    • ComponentActivity 监听onDestroy ,清理
    • Fragment 在FragmentActivity 的 onDestroy 会清理。
  • ViewModelStore 知道么?

    • 知道 ,一个map 就是干。
  • 知道怎么创建的么?

    • 内部 factory 反射就是干。
  • 为啥旋转还能保存数据?

    判断了是配置变化,如旋转屏幕等,等到真正销毁才清空。

  • 剩下的自行发挥

相关推荐
服装学院的IT男1 小时前
【Android 13源码分析】Activity生命周期之onCreate,onStart,onResume-2
android
Arms2062 小时前
android 全面屏最底部栏沉浸式
android
服装学院的IT男2 小时前
【Android 源码分析】Activity生命周期之onStop-1
android
ChinaDragonDreamer4 小时前
Kotlin:2.0.20 的新特性
android·开发语言·kotlin
网络研究院6 小时前
Android 安卓内存安全漏洞数量大幅下降的原因
android·安全·编程·安卓·内存·漏洞·技术
凉亭下7 小时前
android navigation 用法详细使用
android
小比卡丘9 小时前
C语言进阶版第17课—自定义类型:联合和枚举
android·java·c语言
前行的小黑炭10 小时前
一篇搞定Android 实现扫码支付:如何对接海外的第三方支付;项目中的真实经验分享;如何高效对接,高效开发
android
落落落sss11 小时前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
代码敲上天.12 小时前
数据库语句优化
android·数据库·adb