快捷搜索:  汽车  科技

activity无法改原始控件(为什么Activity都重建了)

activity无法改原始控件(为什么Activity都重建了)示例程序class MainActivity : AppCompatActivity() { private val model: NameViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // LiveData 观察者 val nameObserver = Observer<String> { newName -> // 更新视图 nameTextView.text = newName

1. 认识 ViewModel1.1 为什么要使用 ViewModel?

ViewModel 的作用可以区分 2 个维度来理解:

  • 1、界面控制器维度: 在最初的 MVC 模式中,Activity / Fragment 中承担的职责过重,因此,在后续的 UI 开发模式中,我们选择将 Activity / Fragment 中与视图无关的职责抽离出来,在 MVP 模式中叫作 Presenter,在 MVVM 模式中叫作 ViewModel。因此,我们使用 ViewModel 来承担界面控制器的职责,并且配合 LiveData / Flow 实现数据驱动。
  • 2、数据维度: 由于 Activity 存在因配置变更销毁重建的机制,会造成 Activity 中的所有瞬态数据丢失,例如网络请求得到的用户信息、视频播放信息或者异步任务都会丢失。而 ViewModel 能够应对 Activity 因配置变更而重建的场景,在重建的过程中恢复 ViewModel 数据,从而降低用户体验受损。

关于 MVVM 等模式的更多内容,我们在 5、从 MVC 到 MVP、MVVM、MVI:Android UI 架构演进 这篇文章讨论过。

MVVM 模式示意图:

activity无法改原始控件(为什么Activity都重建了)(1)

MVI 模式示意图:

activity无法改原始控件(为什么Activity都重建了)(2)

ViewModel 生命周期示意图:

activity无法改原始控件(为什么Activity都重建了)(3)

1.2 ViewModel 的使用方法
  • 1、添加依赖: 在 build.gradle 中添加 ViewModel 依赖,需要注意区分过时的方式:

// 过时方式(lifecycle-extensions 不再维护) implementation "androidx.lifecycle:lifecycle-extensions:2.4.0" // 目前的方式: def lifecycle_version = "2.5.0" // Lifecycle 核心类 implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version" // LiveData implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" // ViewModel implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" 复制代码

  • 2、模板代码: ViewModel 通常会搭配 LiveData 使用,以下为使用模板,相信大家都很熟悉了:

NameViewModel.kt

class NameViewModel : ViewModel() { val currentName: MutableLiveData<String> by lazy { MutableLiveData<String>() } } 复制代码

MainActivity.kt

class MainActivity : AppCompatActivity() { private val model: NameViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // LiveData 观察者 val nameObserver = Observer<String> { newName -> // 更新视图 nameTextView.text = newName } // 注册 LiveData 观察者,this 为生命周期宿主 model.currentName.observe(this nameObserver) // 修改 LiveData 数据 button.setOnClickListener { val anotherName = "John Doe" model.currentName.value = anotherName } } } 复制代码1.3 ViewModel 的创建方式

创建 ViewModel 实例的方式主要有 3 种,它们最终都是通过第 1 种 ViewModelProvider 完成的:

  • 方法 1: ViewModelProvider 是创建 ViewModel 的工具类:

示例程序

// 不带工厂的创建方式 val vm = ViewModelProvider(this).get(MainViewModel::class.java) // 带工厂的创建方式 val vmWithFactory = ViewModelProvider(this MainViewModelFactory()).get(MainViewModel::class.java) // ViewModel 工厂 class MainViewModelFactory( ) : ViewModelProvider.Factory { private val repository = MainRepository() override fun <T : ViewModel> create(modelClass: Class<T>): T { return MainViewModel(repository) as T } } 复制代码

  • 方法 2: 使用 Kotlin by 委托属性,本质上是间接使用了 ViewModelProvider:

示例程序

// 在 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() } 复制代码

  • 方法 3: Hilt 提供了注入部分 Jetpack 架构组件的支持

示例程序

@HiltAndroidApp class DemoApplication : Application() { ... } @HiltViewModel class MainViewModel @Inject constructor() : ViewModel() { ... } @AndroidEntryPoint class MainHiltActivity : AppCompatActivity(){ val viewModel by viewModels<MainViewModel>() ... } 复制代码

依赖项

// Hilt ViewModel 支持 implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0" // Hilt 注解处理器 kapt "androidx.hilt:hilt-compiler:1.0.0" 复制代码

需要注意的是,虽然可以使用依赖注入普通对象的方式注入 ViewModel,但是这相当于绕过了 ViewModelProvider 来创建 ViewModel。这意味着 ViewModel 实例一定不会存放在 ViewModelStore 中,将失去 ViewModel 恢复界面数据的特性。

错误示例

@AndroidEntryPoint class MainHiltActivity : AppCompatActivity(){ @Inject lateinit var viewModel : MainViewModel } 复制代码2. ViewModel 实现原理分析2.1ViewModel 的创建过程

上一节提到,3 种创建 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 实例。

ViewModelProvider.java

// 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(); } } 复制代码

ViewModelStore.java

// 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 接口的实现类,例如 Activity:

ViewModelStoreOwner.java

public interface ViewModelStoreOwner { @NonNull ViewModelStore getViewModelStore(); } 复制代码

androidx.activity.ComponentActivity.java

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; } } 复制代码

点击链接查看原文,获取更多福利!

https://developer.aliyun.com/article/1063417?utm_content=g_1000362250

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

猜您喜欢: