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

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

  • 剩下的自行发挥

相关推荐
雨白9 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹10 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空12 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭12 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日13 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安13 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑14 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟18 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡19 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0019 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体