Android ViewModel创建不受横竖屏切换影响原理详解

作者:大强Dev 时间:2023-08-31 21:21:20 

ViewModel的创建方式

在我们项目中, 引入了viewModel 做MVI 设计模式的组成部分,它是JetPack 组件库中的重要成员。今天来了解下它。

// 在 Activity 中使用
class MainActivity : AppCompatActivity() {
  // 使用 Activity 的作用域
  private val viewModel : MainViewModel by viewModels()
}
// 在 Fragment 中使用
class MainFragment : Fragment() {
  // 使用 Activity 的作用域,与 MainActivity 使用同一个对象
  val activityViewModel : MainViewModel by activityViewModels()
  // 使用 Fragment 的作用域
  val viewModel : MainViewModel by viewModels()
}
// ViewModel 创建工厂
private final Factory mFactory;
// ViewModel 存储容器
private final ViewModelStore mViewModelStore;
// 默认使用 NewInstanceFactory 反射创建 ViewModel
public ViewModelProvider(ViewModelStoreOwner owner) {
   this(owner.getViewModelStore(), ... NewInstanceFactory.getInstance());
}
// 自定义 ViewModel 创建工厂
public ViewModelProvider(ViewModelStoreOwner owner, Factory factory) {
   this(owner.getViewModelStore(), factory);
}
// 记录宿主的 ViewModelStore 和 ViewModel 工厂
public ViewModelProvider(ViewModelStore store, Factory factory) {
   mFactory = factory;
   mViewModelStore = store;
}
@NonNull
@MainThread
public <T extends ViewModel> T get(Class<T> modelClass) {
   String canonicalName = modelClass.getCanonicalName();
   if (canonicalName == null) {
       throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
   }
   // 使用类名作为缓存的 KEY
   return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
// Fragment
@NonNull
@MainThread
public <T extends ViewModel> T get(String key, Class<T> modelClass) {
   // 1. 先从 ViewModelStore 中取缓存
   ViewModel viewModel = mViewModelStore.get(key);
   if (modelClass.isInstance(viewModel)) {
       return (T) viewModel;
   }
   // 2. 使用 ViewModel 工厂创建实例
   viewModel = mFactory.create(modelClass);
   ...
   // 3. 存储到 ViewModelStore
   mViewModelStore.put(key, viewModel);
   return (T) viewModel;
}
// 默认的 ViewModel 工厂
public static class NewInstanceFactory implements Factory {
   private static NewInstanceFactory sInstance;
   @NonNull
   static NewInstanceFactory getInstance() {
       if (sInstance == null) {
           sInstance = new NewInstanceFactory();
       }
       return sInstance;
   }
   @NonNull
   @Override
   public <T extends ViewModel> T create(Class<T> modelClass) {
       // 反射创建 ViewModel 对象
       return modelClass.newInstance();
   }
}

可以看到:

ViewModel 实例的方法最终是通过 ViewModelProvider 完成的。ViewModelProvider 可以理解为创建 ViewModel 的工具类,它需要 2 个参数:

参数 1 ViewModelStoreOwner:

它对应于 Activity / Fragment 等持有 ViewModel 的宿主,它们内部通过 ViewModelStore 维持一个 ViewModel 的映射表,ViewModelStore 是实现 ViewModel 作用域和数据恢复的关键;

参数 2 Factory:

它对应于 ViewModel 的创建工厂,缺省时将使用默认的 NewInstanceFactory 工厂来反射创建 ViewModel 实例。

创建 ViewModelProvider 工具类后,你将通过 get() 方法来创建 ViewModel 的实例。get() 方法内部首先会通过 ViewModel 的全限定类名从映射表(ViewModelStore)中取缓存,未命中才会通过 ViewModel 工厂创建实例再缓存到映射表中。 正因为同一个 ViewModel 宿主使用的是同一个 ViewModelStore 映射表,因此在同一个宿主上重复调用 ViewModelProvider.get() 返回同一个 ViewModel 实例。

// ViewModel 本质上就是一个映射表而已
public class ViewModelStore {
  // <String - ViewModel> 哈希表
  private final HashMap<String, ViewModel> mMap = new HashMap<>();
  final void put(String key, ViewModel viewModel) {
      ViewModel oldViewModel = mMap.put(key, viewModel);
      if (oldViewModel != null) {
          oldViewModel.onCleared();
      }
  }
  final ViewModel get(String key) {
      return mMap.get(key);
  }
  Set<String> keys() {
      return new HashSet<>(mMap.keySet());
  }
  public final void clear() {
      for (ViewModel vm : mMap.values()) {
          vm.clear();
      }
      mMap.clear();
  }
}

ViewModel 宿主是 ViewModelStoreOwner 接口的实现类,例如 ComponentActivity:

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
   ContextAware,
   LifecycleOwner,
   ViewModelStoreOwner ... {
   // ViewModel 的存储容器
   private ViewModelStore mViewModelStore;
   // ViewModel 的创建工厂
   private ViewModelProvider.Factory mDefaultFactory;
   @NonNull
   @Override
   public ViewModelStore getViewModelStore() {
       if (mViewModelStore == null) {
           // 已简化过程
           mViewModelStore = new ViewModelStore();
       }
       return mViewModelStore;
   }
}

由此,ViewModel 的创建其实跟activity 是相关联的。

ViewModel 为什么不受 Activity 横竖屏生命周期的影响

1、在 Activity 走到 onDestroy 方法时,做了判断 isChangingConfigurations

Android ViewModel创建不受横竖屏切换影响原理详解

可以看到,在 ComponentActivity 构造方法中添加了生命周期的判断,当 Activity onDestroy 时,如果是发生了横竖屏切换,就不会走 getViewModelStore().clear(),清理操作,保证了ViewModel 持有的数据还能存在。

2、在 Activity 获取 getViewModelStore 时,

Android ViewModel创建不受横竖屏切换影响原理详解

Android ViewModel创建不受横竖屏切换影响原理详解

通过getLastNonConfigurationInstance() 获取 NonConfigurationInstances 实例,从而得到这个实例中的 viewModelStore

Android ViewModel创建不受横竖屏切换影响原理详解

而且,Activity 生命周期的变化都会走这个方法,来保证viewModelStore 不为空。

Android ViewModel创建不受横竖屏切换影响原理详解

3、onRetainNonConfigurationInstance 调用

在 Activity 屏幕旋转时,onRetainNonConfigurationInstance()onStoponDestroy之间调用

onRetainNonConfigurationInstance() 是个重要的方法

Android ViewModel创建不受横竖屏切换影响原理详解

这保证了 在销毁前,viewModelStore 实例被拿到并交给 NonConfigurationInstances 实例,将 viewModelStore 赋值给他

Android ViewModel创建不受横竖屏切换影响原理详解

来源:https://juejin.cn/post/7207689082183696442

标签:Android,ViewModel,创建,横竖屏切换
0
投稿

猜你喜欢

  • hibernate 命名查询如何实现

    2023-11-04 02:35:41
  • JavaSE实现猜拳游戏

    2023-12-22 07:21:19
  • Android获取常用辅助方法(获取屏幕高度、宽度、密度、通知栏高度、截图)

    2023-10-30 19:42:57
  • Android 音乐播放器的开发实例详解

    2023-12-07 17:53:10
  • Java 全面系统介绍反射的运用

    2021-12-18 22:51:30
  • JAVA中JSONObject对象和Map对象之间的相互转换

    2023-07-13 15:04:28
  • C#图表算法之无向图

    2021-08-08 22:50:20
  • eclipse下搭建hibernate5.0环境的步骤(图文)

    2022-09-26 02:48:57
  • Springboot项目打war包docker包找不到resource下静态资源的解决方案

    2022-01-01 07:03:55
  • c#生成高清缩略图的二个示例分享

    2023-04-09 23:21:46
  • Java基础之final关键字作用案例

    2022-11-02 19:23:35
  • Android实现WebView点击拦截跳转原生

    2023-01-06 23:47:38
  • Java程序图形用户界面设计之按钮与布局

    2023-07-18 07:03:21
  • SpringBoot对Druid配置SQL监控功能失效问题及解决方法

    2023-06-10 21:31:24
  • Android之有效防止按钮多次重复点击的方法(必看篇)

    2022-03-15 01:30:34
  • SpringBoot详细讲解静态资源导入的实现

    2023-07-26 13:23:21
  • Android中使用 AutoCompleteTextView 实现手机号格式化附带清空历史的操作

    2021-07-05 17:08:43
  • C#中Action和Func的区别

    2023-10-20 01:30:08
  • JVM完全解读之GC日志记录分析

    2022-09-22 12:43:08
  • Android语音识别技术详解及实例代码

    2022-09-23 03:46:33
  • asp之家 软件编程 m.aspxhome.com