聊聊ViewModel
2021-12-19 本文已影响0人
济公大将
ViewModel是啥,咋用?
官方翻译其实就是最好的理解,一下是对官方注释的翻译,英文大佬勿喷。
定义
- ViewModel 是一个负责为Activity或Fragment准备和管理数据的类。它还处理Activity或Fragment与应用恒旭其它部分的通信(例如掉哟用业务逻辑类)。
- ViewModel总是与作用域(一个Fragment或者Activity)相关联创建的,并且只要作用域处于活动状态ViewModel就会被保留。例如:如果其作用域是Activity的话,知道Activity被finish,关联它的ViewModel才会被销毁。换句话说,这也就意味着如果ViewModel的所有者因为配置个更改(例如旋转屏幕)而被销毁,那么它将不会被销毁。多有这的新实例将重新连接到现有的ViewModel。
- ViewModel的目的是获取或保存Activity或Fragment所必需的信息。Activity或Fragment应该能够观察ViewModel中的更改。ViewModel通常通过LiveData或者Android Data Binding公开这些信息。当然这个并非一定,您也可以有自己喜欢的框架的来实现。
- ViewModel的唯一职责是管理UI的数据。它不应该访问你的视图层次结构或持有一个Activity或Fragment的引用。
使用
- 定义ViewModel
public class UserModel extends ViewModel { private final MutableLiveData<User> userLiveData = new MutableLiveData<>(); public LiveData<User> getUser() { return userLiveData; } public UserModel() { // trigger user load. } void doAction() { // depending on the action, do necessary business logic calls and update the // userLiveData. } }
- 在Activity中使用
//官方这个实例有点脱节的,Activity本身是没有实现LifeCycleOwner和ViewModelStoreOwner接口的,这里正常应该至少是ComponentActivity才行。知道下就好了 public class UserActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.user_activity_layout); final UserModel viewModel = new ViewModelProvider(this).get(UserModel.class); viewModel.getUser().observe(this, new Observer () { @Override public void onChanged(@Nullable User data) { // update ui. } }); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { viewModel.doAction(); } }); } }
- 在Fragment中使用
ViewModel 还可以用作Activity的不同Fragment的通信层。每个Fragment可以通过它们的Activity使用相同的Key来获取ViewModel。这允许Fragments之间以一种解耦的方式进行通信,这样它们就不需要直接与另一个Fragment对话。public class MyFragment extends Fragment { public void onStart() { UserModel userModel = new ViewModelProvider(requireActivity()).get(UserModel.class); } }
结合Ktx库及现有项目中的源码来看看ViewModel是咋创建的,简单代码背后都有些啥?
- 声明一个ViewModel
private val vm:MyViewModel by viewModels()
官方给的声明不是这样的,咋这里就这么一下就完事了?为啥要用by呀?后面那个viewModels()是个什么鬼?
关于By,可以先看Kotlin中的by lazy ,这里直接用两块代码,告诉你by在这里咋回事
- 我们些的Kotlin代码
class MainActivity : AppCompatActivity() { //通过委托属性的方式,声明一个ViewModel对象 private val vm:MyViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) vm.getUserInfo() } }
- 经过bytecode->Decompile后的部分代码
public final class MainActivity extends AppCompatActivity { // 我们声明的ViewModel类型的变量编程了一个类型为Lazy的变量,也就是咱们原来的变量 //委托给了变量 vm$delegate private final Lazy vm$delegate; // 这里是Kotlin语法糖的代码,val变量提供一个getXXX方法 private final MyViewModel getVm() { Lazy var1 = this.vm$delegate; Object var3 = null; //咱们的getVm最终是由委托对象的getValue()方法提供 return (MyViewModel)var1.getValue(); } protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(1300083); //这里调用了getVm()方法 this.getVm().getUserInfo(); } public MainActivity() { Function0 factoryProducer$iv = (Function0)null; int $i$f$viewModels = false; Function0 factoryPromise$iv = (Function0)(new MainActivity$$special$$inlined$viewModels$1(this)); //委托变量在构造方法中进行初始化 this.vm$delegate = (Lazy)(new ViewModelLazy(Reflection.getOrCreateKotlinClass(MyViewModel.class), (Function0)(new MainActivity$$special$$inlined$viewModels$2(this)), factoryPromise$iv)); } }
- viewModels()方法什么鬼,可能从上面的委托变量可以大概看到个影子,不过我们还是从源码中找答案吧,点进去后得到如下代码。
inline noinline crossinline
Kotlin中的by lazy//这里是个ComponentActivity 的扩展方法 //要在主线程使用哦 @MainThread //这句信息量有点大,得知道 inline noinline reified Lazy的相关知识点,看倒链吧 inline fun <reified VM : ViewModel> ComponentActivity.viewModels( noinline factoryProducer: (() -> Factory)? = null // 返回类型是Lazy,这里就对上我们byteCode后的类型了 ): Lazy<VM> { // 这里翻译过来,如果没有给Factory的就用ComponentActivity 的 defaultViewModelProviderFactory val factoryPromise = factoryProducer ?: { defaultViewModelProviderFactory } //最终的实现类是ViewModelLazy,这里对上我们byteCode后的委托对象的赋值操作了 return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise) }
- 接下来看看ViewModelLazy类
//实现了Lazy接口,本质就是实现几个方法 class ViewModelLazy<VM : ViewModel> //成员变量,VM对应的class private val viewModelClass: KClass<VM>, // 返回类型为ViewModelStore函数对象,结合上文其实是Activity的ViewModelStore private val storeProducer: () -> ViewModelStore, //返回类型为ViewModelProvider.Factory 的函数对象,结合上文是Activity的defaultViewModelProviderFactory private val factoryProducer: () -> ViewModelProvider.Factory ) : Lazy<VM> { //缓存,其实这里是Lazy用意的一个标准成员变量,目的是减少重复获取,就是变量的一个内存缓存而已 private var cached: VM? = null // 复写的Lazy的方法,这也是咱们ByteCode里getVM内部委托对象的getValue的真正实现者 override val value: VM get() { // cached赋值,第一次调用cached为null val viewModel = cached // 如果为null return if (viewModel == null) { // 得到工厂 val factory = factoryProducer() // 得到Vm的存储器 val store = storeProducer() // 调用ViewModelProvider的get方法获取ViewModel实例后将值赋值给cached // 初步找到来源了,但是还是不晓得ViewModel咋创建的,接下来就要看这个get了 // 这里能看出和store factory是有关系的 ViewModelProvider(store, factory).get(viewModelClass.java).also { cached = it } } else { //如果内存缓存存在,则直接返回,这里对应了by lazy这种用意的特点,也就是所谓的延时获取,就是只有第一次使用时才会新建啥的,后面内存缓存有了,就直接用内存缓存了 viewModel } } //是否初始化的实现,了解下就好 override fun isInitialized() = cached != null }
- ViewModelProvider(store, factory).get(viewModelClass.java) 看看
超链:SavedStateHandle源码详解@MainThread public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) { //先根据key去stroe找一下,这里有个key默认实现的key是androidx.lifecycle.ViewModelProvider.DefaultKey:canonicalName;, ViewModel viewModel = mViewModelStore.get(key); //这句话可以分为两个理解,一个是viewmodel得不是空,另一个得是modelClass的实例 if (modelClass.isInstance(viewModel)) { //这里是如果咱们的Factory是这个类型的话,则进行onRequery,咱们默认的Factory其实就是这个类型,会有实现,但这是另一大套知识体系了,其实这套体系就是简化原来的系统销毁重建的数据恢复的流程,将操作转义到VM里的,详情看超链吧... if (mFactory instanceof OnRequeryFactory) { ((OnRequeryFactory) mFactory).onRequery(viewModel); } //总之呢,如果存储器根据key拿到了,就直接返回了,草。。。还不是创建 return (T) viewModel; } else { //忘记实现了的废代码... //noinspection StatementWithEmptyBody if (viewModel != null) { // TODO: log a warning. } } //如果是KeyedFactory类型,则通过工厂的create得到对象,很巧,咱们的默认就是这个类型,还得看默认的Factory的实现 if (mFactory instanceof KeyedFactory) { viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass); } else { //如果不是,则直接通过自定义的Factory实现,这就提供了一种无限的创建Model的实例,这里就决定了,我们可以自定义创建Model的过程,方便做架构上的自定义 viewModel = (mFactory).create(modelClass); } //将得到的viewModel存储到store里 mViewModelStore.put(key, viewModel); return (T) viewModel; }
- 通过上面的源码我们知道连个事,一个是ViewModel由ViewModelStore存储,另一个是viewModel由Factory提供,我们先看ViewModelStore是个啥
/** * 1. 灰常easy,里面就是一个简单HashMap */ public class ViewModelStore { private final HashMap<String, ViewModel> mMap = new HashMap<>(); final void put(String key, ViewModel viewModel) { ViewModel oldViewModel = mMap.put(key, viewModel); //这里需要注意下,当put一个已经存在的key的ViewModel对象时,会将原来的ViewModel先执行onCleared()方法,最终用的是后添加的ViewModel //啥情况会这样呢,我们给不同类型ViewModel使用了同样的key时就会走到这,需要结合之前的if (modelClass.isInstance(viewModel))来理解 if (oldViewModel != null) { oldViewModel.onCleared(); } } final ViewModel get(String key) { return mMap.get(key); } Set<String> keys() { return new HashSet<>(mMap.keySet()); } /** * Clears internal storage and notifies ViewModels that they are no longer used. * 清除内部存储并通知ViewModels它们不再使用。将销毁逻辑时会回到这里看 */ public final void clear() { for (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); } }
- 存储的规则知道了,再接着看创建,当然这里我们看的是系统默认提供的创建,因为Factory我们完全可以自定义,这要回到最初的那个默认的defaultViewModelProviderFactory,先看它是咋来的
// CompentActivity实现了HasDefaultViewModelProviderFactory接口 @Override public ViewModelProvider.Factory getDefaultViewModelProviderFactory() { if (getApplication() == null) { throw new IllegalStateException("Your activity is not yet attached to the " + "Application instance. You can't request ViewModel before onCreate call."); } //mDefaultFactory是ComponentActivity的成员变量,且进行了内存缓存 if (mDefaultFactory == null) { // 这里找到了实例为SavedStateViewModelFactory mDefaultFactory = new SavedStateViewModelFactory( getApplication(), this, getIntent() != null ? getIntent().getExtras() : null); } return mDefaultFactory; }
- 接下来就是SavedStateViewModelFactory的研究了,我们只看create哈
@Override public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) { //是否为AndroidViewModel类型,这个我们没在用,其实这个就是多了个getApplication()方法,有时候我们在ViewModel中需要使用Application对象,就可以使用这个AndroidViewModel最为基类 boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass); //反射匹配构造器,这里很有意思,这里是的我们可以支持两种默认构造器的参数而无需自己去实例化它 // 分别为(Application类型,SavedStateHandle类型) 或(SavedStateHandle类型) // 也就是说通过这个工厂创建的ViewModel,可以只声明两个这种类型的参数,而无需传真正的实例,因为会在下面给赋值真正的参数实例 //这是一个很好的借鉴思路哦 Constructor<T> constructor; if (isAndroidViewModel) { constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE); } else { constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE); } // 没有找到规定参数类型的构造器,则交给器内部的mFactory对象创建,咱们平时很少写这种类型的构造参数,所以这里还不是咱们现有代码场景实例的地方。。。草 // doesn't need SavedStateHandle if (constructor == null) { return mFactory.create(modelClass); } //有幸我们知道ViewModel的SavedStateHandle机制,按照系统提供的用了,则就会到这里来了 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) { //此处省略一些抛异常的代码 }
- 从上面知道,那咱们平时的写的ViewModel实例有些不符合默认的构造参数规则,所以是由mFactory.create(modelClass);创建的,通过点点,我们可以知道mFactory的实例为AndroidViewModelFactory,其create方法如下。。。哎呀,创建总算完事了。。。
//就是通过反射调用默认的构造参数创建实例 @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { if (AndroidViewModel.class.isAssignableFrom(modelClass)) { //noinspection TryWithIdenticalCatches try { return modelClass.getConstructor(Application.class).newInstance(mApplication); } catch (NoSuchMethodException e) { //省略... } } return super.create(modelClass); }
我们现在知道创建了,还知道创建的对象被放到了ViewModelStore的HashMap中,那这个ViewModelStore对象到底是咋来的,又是如何存储的呢?接下来我们就通过ViewModelStore对象的来龙去脉解开ViewModel配置更改无关性的神秘面纱!(这里只看Activity的)
- ViewModelStore的初创建
//实现了ViewModelStoreOwner接口的方法 @Override public ViewModelStore getViewModelStore() { //没有Application,抛异常...这里应该是防止自己Hook时时机错误的 if (getApplication() == null) { throw new IllegalStateException("Your activity is not yet attached to the " + "Application instance. You can't request ViewModel before onCreate call."); } //如果 对象为null,则拿到NonConfigurationInstances实例,并将其成员变量赋值给mViewModelStore if (mViewModelStore == null) { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { // Restore the ViewModelStore from NonConfigurationInstances‘ //从这里可以看出,viewModelStore会被存储在NonConfigurationInstances,以便被恢复 mViewModelStore = nc.viewModelStore; } //如果上面的恢复失败,则会直接new一个 if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } return mViewModelStore; }
- 从上面知道mViewModel是从NonConfigurationInstances中恢复的,那mViewModel合适给的这哥们呢?
//NonConfigurationInstances是ComponentActivity的一个静态内部类, //viewModelStore是其成员,通过点,能找到是在onRetainNonConfigurationInstance赋值的 //注释翻译过来:保留所有适当的非配置状态。 //你不能自己重写它! //如果你想保留自己的非配置状态,请使用android .lifecycle. viewmodel。 @Override @Nullable public final Object onRetainNonConfigurationInstance() { //这里其实是个辅助性实现,官方的意思是,用androidx就不要重写onRetainNonConfigurationInstance //直接用系统的实现,通过ViewModel去存储配置无关状态 //假如你就是不想用ViewModel,或者你压根没有引入androidx,可以custom(自己去折腾),非要这样的话 //重写onRetainCustomNonConfigurationInstance去,也别重写onRetainNonConfigurationInstance //用心良苦呀,注释的很清楚 Object custom = onRetainCustomNonConfigurationInstance(); //先将成员给临时变脸 ViewModelStore viewModelStore = mViewModelStore; if (viewModelStore == null) { // 一次都没有调用过getViewModelStore(),mViewModelStore肯定是null // 就先尝试从最后的NonConfigurationInstance里去拿 NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { viewModelStore = nc.viewModelStore; } } // 都没有。。。那还保留个J,直接return if (viewModelStore == null && custom == null) { return null; } //只要有一个有效的需要保留的成员,则打包下就好 NonConfigurationInstances nci = new NonConfigurationInstances(); nci.custom = custom; nci.viewModelStore = viewModelStore; return nci; }
- 通过上面,我们能感觉得出通过onRetainNonConfigurationInstance()来保留配置无关实例而后通过getLastNonConfigurationInstance()来恢复配置无关实例。那么接下来我们先看到底最终保留到哪里了!
// onRetainNonConfigurationInstance是在Activity的retainNonConfigurationInstances调用的 NonConfigurationInstances retainNonConfigurationInstances() { //通过下面三行代码我们能看到我们Activity的NonConfigurationInstance只是配置无关中的一个成员而已。 //同时还能发现最终被存储的是Object类型,这是官方的一个思想,针对Activity我提供了配置无关数据的保留方案,至于实例类型是啥,俺不管 Object activity = onRetainNonConfigurationInstance(); HashMap<String, Object> children = onRetainNonConfigurationChildInstances(); //看到了FragmentManagerNoConfig影子,其实可以联想到,Fragment的ViewModel大概也是在这里被保留的 FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig(); // We're already stopped but we've been asked to retain. // 我们已经停止了,但我们被要求保留。 // Our fragments are taken care of but we need to mark the loaders for retention. // 我们的fragments已经处理好了,但我们需要在装载机上做标记以便保留。 // In order to do this correctly we need to restart the loaders first before handing them off to the next activity. // 为了正确地做到这一点,我们需要首先重新启动加载器,然后再将它们移交给下一个活动 mFragments.doLoaderStart(); mFragments.doLoaderStop(true); ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig(); //啥也没有 if (activity == null && children == null && fragments == null && loaders == null && mVoiceInteractor == null) { return null; } //将需要保留的配置无关的各种实例进行打包 NonConfigurationInstances nci = new NonConfigurationInstances(); nci.activity = activity; nci.children = children; nci.fragments = fragments; nci.loaders = loaders; if (mVoiceInteractor != null) { mVoiceInteractor.retainInstance(); nci.voiceInteractor = mVoiceInteractor; } return nci; }
- 上面这个方法,体现了我们android层哪些是配置无关的东西,以及被打包成了NonConfigurationInstances实例,真正的保存还得看这个方法被谁调了,可惜点不动了,网上找找看,发现其是在下面调用。注意,这个先正着看,再倒着分析才能恍然大悟。
//mActivities 一个ArryMap,存储的ActivityClientRecord,是ActivityThread的成员变量 // ActivityThread 的成员变量,生命周期大概率老长了 final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>(); ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) { // r 存储在mActivities中 ActivityClientRecord r = mActivities.get(token); //...此处略去789个字 //简单了解过AMS的知道,performDestroyActivity这个方法是在销毁是要走的方法 // 也就是说等Activity销毁的时候,咱们的那个NonConfigurationInstances实例会被保留 // 保留在了r的lastNonConfigurationInstances实例下,这个lastXXX是不是很眼熟(getLastNonConfigurationInstance) try { r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances(); } catch (Exception e) { } //...此处再略去999个字 //mInstrumentation.callActivityOnDestroy(r.activity); }
- 到此我们知道了,最终咱们的NonConfigurationInstances是在Activity销毁是存到了ActivityThread的成员变量的mActivies下,算是打通了存储流程。接下来就看恢复了。
从前面我们知道,恢复的入口是getLastNonConfigurationInstance()@Nullable public Object getLastNonConfigurationInstance() { //mLastNonConfigurationInstances存储的我们Activity下的NonConfigurationInstance return mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.activity : null; } //mLastNonConfigurationInstances 是Activity的成员变量 NonConfigurationInstances mLastNonConfigurationInstances; // mLastNonConfigurationInstances 在attach方法中被赋值 @UnsupportedAppUsage final void attach(xxx,NonConfigurationInstances lastNonConfigurationInstances,xxx){ mLastNonConfigurationInstances = lastNonConfigurationInstances; } //attach是在performLaunchActivity方法中被调用的,可以看到咱们的 //lastNonConfigurationInstances是从ActivityClientRecord.lastNonConfigurationInstances恢复过来的 // 基于基础的AMS,performLaunchActivity是在启动Activity时调用的,而ActivityClientRecord是从AMS恢复而来的 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { //...此处略去1w字 activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback, r.assistToken); //此处略去1w字 synchronized (mResourcesManager) { //最终ActivityClientRecord实例会存储到咱们那个mActivities的Map中 //这里能打通为啥performDestroyActivity中的get(token)为啥有值了 mActivities.put(r.token, r); } }
- 至此我们就打通了这个配置无关的来龙去脉了吧。这个过程我们发现这套机制是可以自定义的,为我们日后的扩展提供了一个思路。
创建,存储,恢复,我们都晓得了,那我们平时的onCleared()到底啥时候执行呢viewModel啥时候被回收呢,从上面我们可以看看那个ViewModelStore的Map合适clear就好了。
public ComponentActivity() { //... getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { //生命周期组件的ON_DESTROY时候且不是isChangingConfigurations的时候才clear //isChangingConfigurations这个哥们配置更改重建过程时会为true,也就是配置更改我们发现走了onDestroy但是并没有走onCleared()方法 //只有真正的销毁,比如按了返回键,才会真正的执行clear,也就是回收 if (event == Lifecycle.Event.ON_DESTROY) { if (!isChangingConfigurations()) { getViewModelStore().clear(); } } } }); //... } public class ViewModelStore { //.... public final void clear() { for (ViewModel vm : mMap.values()) { //挨个调用VM.clear vm.clear(); } //清除集合 mMap.clear(); } } public abstract class ViewModel { @MainThread final void clear() { mCleared = true; //... //调用onCleared() onCleared(); } }
总结一下就是Activity在onDestroy前执行onCleared()