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 反射就是干。
  • 为啥旋转还能保存数据?

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

  • 剩下的自行发挥

相关推荐
氤氲息1 小时前
Android 底部tab,使用recycleview实现
android
Clockwiseee1 小时前
PHP之伪协议
android·开发语言·php
小林爱1 小时前
【Compose multiplatform教程08】【组件】Text组件
android·java·前端·ui·前端框架·kotlin·android studio
小何开发2 小时前
Android Studio 安装教程
android·ide·android studio
开发者阿伟3 小时前
Android Jetpack LiveData源码解析
android·android jetpack
weixin_438150993 小时前
广州大彩串口屏安卓/linux触摸屏四路CVBS输入实现同时显示!
android·单片机
CheungChunChiu4 小时前
Android10 rk3399 以太网接入流程分析
android·framework·以太网·eth·net·netd
木头没有瓜4 小时前
ruoyi 请求参数类型不匹配,参数[giftId]要求类型为:‘java.lang.Long‘,但输入值为:‘orderGiftUnionList
android·java·okhttp
键盘侠0074 小时前
springboot 上传图片 转存成webp
android·spring boot·okhttp
江上清风山间明月5 小时前
flutter bottomSheet 控件详解
android·flutter·底部导航·bottomsheet