美文网首页
ViewModel之构造

ViewModel之构造

作者: 就叫汉堡吧 | 来源:发表于2021-09-08 22:15 被阅读0次
    • 概述

      ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。

      Android 框架可以管理界面控制器(如 Activity 和 Fragment)的生命周期。Android 框架可能会决定销毁或重新创建界面控制器,以响应完全不受您控制的某些用户操作或设备事件。

      如果系统销毁或重新创建界面控制器,则存储在其中的任何瞬态界面相关数据都会丢失。例如,应用可能会在它的某个 Activity 中包含用户列表。为配置更改重新创建 Activity 后,新 Activity 必须重新提取用户列表。对于简单的数据,Activity 可以使用 onSaveInstanceState() 方法从 onCreate() 中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。

      另一个问题是,界面控制器经常需要进行可能需要一些时间才能返回的异步调用。界面控制器需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在为配置更改重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。

      诸如 Activity 和 Fragment 之类的界面控制器主要用于显示界面数据、对用户操作做出响应或处理操作系统通信(如权限请求)。如果要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀。为界面控制器分配过多的责任可能会导致单个类尝试自己处理应用的所有工作,而不是将工作委托给其他类。以这种方式为界面控制器分配过多的责任也会大大增加测试的难度。

      从界面控制器逻辑中分离出视图数据所有权的操作更容易且更高效。

    • 直接方式

      ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()).create(OrderDetailVM.class)
      
      public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
       private static ViewModelProvider.AndroidViewModelFactory sInstance;
       private Application mApplication;
      
       @NonNull
       public static ViewModelProvider.AndroidViewModelFactory getInstance(@NonNull Application application) {
           if (sInstance == null) {
               sInstance = new ViewModelProvider.AndroidViewModelFactory(application);
           }
      
           return sInstance;
       }
      
       public AndroidViewModelFactory(@NonNull Application application) {
           this.mApplication = application;
       }
      
       @NonNull
       public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
           if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
               try {
                   return (ViewModel)modelClass.getConstructor(Application.class).newInstance(this.mApplication);
               } catch (NoSuchMethodException var3) {
                   throw new RuntimeException("Cannot create an instance of " + modelClass, var3);
               } catch (IllegalAccessException var4) {
                   throw new RuntimeException("Cannot create an instance of " + modelClass, var4);
               } catch (InstantiationException var5) {
                   throw new RuntimeException("Cannot create an instance of " + modelClass, var5);
               } catch (InvocationTargetException var6) {
                   throw new RuntimeException("Cannot create an instance of " + modelClass, var6);
               }
             } else {
               return super.create(modelClass);
             }
        }
      }
      

      如果自定义ViewModel如果不是继承自AndroidViewModel的话(但是也是直接或间接继承自ViewModel)会调用父类NewInstanceFactory的create方法:

      public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
       //noinspection TryWithIdenticalCatches
       try {
           return modelClass.newInstance();
       } catch (InstantiationException e) {
           throw new RuntimeException("Cannot create an instance of " + modelClass, e);
       } catch (IllegalAccessException e) {
           throw new RuntimeException("Cannot create an instance of " + modelClass, e);
       }
      }
      
    • 通过KTX拓展构造

      首先需要加入依赖项:

      api 'androidx.fragment:fragment-ktx:1.2.5'
      

      然后在Fragemnt或者Activity中使用viewModels内联函数:

      private val mViewModel by viewModels<AcViewModel>()
      

      你也可以使用“=”直接赋值,这里通过懒加载赋值。

      @MainThread
      inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
        noinline factoryProducer: (() -> Factory)? = null
      ): Lazy<VM> {
        val factoryPromise = factoryProducer ?: {
            defaultViewModelProviderFactory
        }
      
        return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
      }
      

      viewModels方法可以传入一个返回类型是androidx.lifecycle.ViewModelProvider.Factory的函数变量实例,不传的话默认使用ComponentActivity下的defaultViewModelProviderFactory方法。返回一个ViewModelLazy对象:

      class ViewModelLazy<VM : ViewModel> (
        private val viewModelClass: KClass<VM>,
        private val storeProducer: () -> ViewModelStore,
        private val factoryProducer: () -> ViewModelProvider.Factory
      ) : Lazy<VM> {
        private var cached: VM? = null
      
        override val value: VM
            get() {
                val viewModel = cached
                return if (viewModel == null) {
                    val factory = factoryProducer()
                    val store = storeProducer()
                    ViewModelProvider(store, factory).get(viewModelClass.java).also {
                        cached = it
                    }
                } else {
                    viewModel
                }
            }
      
        override fun isInitialized() = cached != null
      }
      

      可以看到,这里有一个cached变量用于缓存,缓存失效的话会通过ViewModelProvider(store, factory).get(viewModelClass.java)去构造:

      @NonNull
      @MainThread
      public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);
      
        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
      }
      

      可以看到,首先会从mViewModelStore中尝试取ViewModel,没取到的话就通过mFactory创建并存入mViewModelStore中,mViewModelStore是通过前面的ComponentActivity的getViewModelStore()方法产生的:

      @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.");
        }
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
      }
      

      这里的mFactory就是前面的getDefaultViewModelProviderFactory()产生的:

      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.");
        }
        if (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
      }
      

      可以看到,默认的factory是SavedStateViewModelFactory,注意这里的第三个参数,获取了Intent的Bundle参数,它的create如下:

      public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
        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());
        }
      }
      
      private static final Class<?>[] ANDROID_VIEWMODEL_SIGNATURE = new Class[]{Application.class,SavedStateHandle.class};
      private static final Class<?>[] VIEWMODEL_SIGNATURE = new Class[]{SavedStateHandle.class};
      

      这里会首先尝试查找两个构造方法,一个是(Application,SavedStateHandle),一个是(SavedStateHandle),实际上这个方法就是为了构造含有SavedStateHandle的ViewModel实例,如果没有定义这两个方法,则会通过ViewModelProvider.AndroidViewModelFactory的create创建,也就是传统方式中的创建方法。

      在构造ViewModel的过程中把defaultArgs放在SavedStateHandle中,然后newInstance的时候传入作为第二个参数,所以前面的构造方法中第二个参数为SavedStateHandle类型来接收。

      那为什么我们不使用直接构造的方式创建ViewModel对象呢?一个原因是封装new的过程,但更重要的是和组件的生命周期绑定在一起。

    • 生命周期绑定监听

      viewmodel-lifecycle

      ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle。ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 activity,是在 activity 完成时;而对于 fragment,是在 fragment 分离时。

      代码中ViewModel绑定生命周期只有在含有SavedStateHandle构造方法的构造方式时才会执行,即:

      SavedStateHandleController controller = SavedStateHandleController.create(
              mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
      
      static SavedStateHandleController create(SavedStateRegistry registry, Lifecycle lifecycle,
              String key, Bundle defaultArgs) {
          Bundle restoredState = registry.consumeRestoredStateForKey(key);
          SavedStateHandle handle = SavedStateHandle.createHandle(restoredState, defaultArgs);
          SavedStateHandleController controller = new SavedStateHandleController(key, handle);
          controller.attachToLifecycle(registry, lifecycle);
          tryToAddRecreator(registry, lifecycle);
          return controller;
      }
      
      void attachToLifecycle(SavedStateRegistry registry, Lifecycle lifecycle) {
          if (mIsAttached) {
              throw new IllegalStateException("Already attached to lifecycleOwner");
          }
          mIsAttached = true;
          lifecycle.addObserver(this);
          registry.registerSavedStateProvider(mKey, mHandle.savedStateProvider());
      }
      
      @Override
      public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
          if (event == Lifecycle.Event.ON_DESTROY) {
              mIsAttached = false;
              source.getLifecycle().removeObserver(this);
          }
      }
      

      可以看到,attachToLifecycle将此Observer绑定到ComponentActivity的mObserverMap中,在组件onDestroy的时候解除引用,避免内存泄露。

      上面create方法中还有一句viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller),它是把和生命周期等相关的参数组成的controller都放到ViewModel的一个叫mBagOfTags的Map中,那么这有什么用呢?

      在ComponentActivity的构造函数中有一段代码:

      getLifecycle().addObserver(new LifecycleEventObserver() {
          @Override
          public void onStateChanged(@NonNull LifecycleOwner source,
                  @NonNull Lifecycle.Event event) {
              if (event == Lifecycle.Event.ON_DESTROY) {
                  if (!isChangingConfigurations()) {
                      getViewModelStore().clear();
                  }
              }
          }
      });
      

      所以在onDestroy的时候viewModelStore会清理所有的ViewModel:

      public final void clear() {
          for (ViewModel vm : mMap.values()) {
              vm.clear();
          }
          mMap.clear();
      }
      

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

      可以看到,这里其实是对Closeable类型的关闭操作,可以重写onCleared添加自己的清理操作。

    相关文章

      网友评论

          本文标题:ViewModel之构造

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