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