目录
ViewModel不会因Configuration Change(如屏幕旋转)而销毁
前言
ViewModel作为Jetpack架构组件的核心成员,在Android应用开发中承担着UI数据管理与生命周期感知的重要职责。理解ViewModel的设计原理和正确使用方式,对于构建健壮、可维护的Android应用至关重要。
使用viewModel有以下优点:
- 将View中的UI界面数据和业务管理分离开,降低耦合性
- 可感知生命周期的组件。
- 不会因配置(如屏幕旋转)Configuration Change改变而销毁。
- 可以配合 LiveData 使用。
- 多个 Fragment 可以共享同一 ViewModel。
- 等等······
使用ViewModel需要了解MVVM模式
MVVM很好理解的,感兴趣的可以阅读这篇文章,我这里就借用大佬的内容:
MVVM 将代码划分为三个部分:
- View: Activity 和 Layout XML 文件,与 MVP 中 View 的概念相同;
- Model: 负责管理业务数据逻辑,如网络请求、数据库(Repository)处理,与 MVP 中 Model 的概念相同;
- ViewModel: 存储视图状态,负责处理表现逻辑,并将数据设置给可观察数据容器。
在实现细节上,View 和 Presenter 从双向依赖变成 View 可以向 ViewModel 发指令,但 ViewModel 不会直接向 View 回调,而是让 View 通过观察者的模式去监听数据的变化,有效规避了 MVP 双向依赖的缺点。但 MVVM 本身也存在一些缺点:
- 多数据流: View 与 ViewModel 的交互分散,缺少唯一修改源,不易于追踪;
- LiveData 膨胀: 复杂的页面需要定义多个 MutableLiveData,并且都需要暴露为不可变的 LiveData。
编辑
一、ViewModel怎么使用
1)、创建一个基础ViewModel类
// 基础ViewModel类
class BaseViewModel : ViewModel() {
// 使用LiveData管理UI状态
private val _uiState = MutableLiveData<UiState>()
val uiState: LiveData<UiState> = _uiState
// 使用协程管理异步操作
private val viewModelScope = CoroutineScope(Dispatchers.Main + Job())
// 错误处理
private val _error = MutableLiveData<String>()
val error: LiveData<String> = _error
// 加载状态
private val _isLoading = MutableLiveData<Boolean>()
val isLoading: LiveData<Boolean> = _isLoading
// 清理资源
override fun onCleared() {
super.onCleared()
viewModelScope.cancel()
}
}
// 用户相关ViewModel
class UserViewModel : BaseViewModel() {
private val userRepository: UserRepository = UserRepository()
// 可以放入用户数据
private val _userData = MutableLiveData<User>()
val userData: LiveData<User> = _userData
// 通过异步处理加载用户数据
fun loadUserData(userId: String) {
viewModelScope.launch {
try {
_isLoading.value = true
val user = userRepository.getUser(userId)
_userData.value = user
} catch (e: Exception) {
_error.value = e.message
} finally {
_isLoading.value = false
}
}
}
}
2)、在Actvivity/Fragment中使用ViewModel
class UserFragment : Fragment() {
private lateinit var userViewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 在onCreate中通过ViewModelProvider方法初始化ViewModel
//如果有参数传入可以重写Factory接口
userViewModel = ViewModelProvider(this).get(UserViewModel::class.java)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 观察数据变化
observeViewModel()
}
private fun observeViewModel() {
// 通过observe观察者模式观察用户数据
userViewModel.userData.observe(viewLifecycleOwner) { user ->
// 更新UI
updateUserUI(user)
}
}
}
3)、使用Factory接口实现带有参数的ViewModel
若想传入数据,可以重写Factory接口,ViewModel默认使用无参数创建。
// 用户相关ViewModel
class UserViewModel(private val userRepository: UserRepository) : BaseViewModel() {
// 可以放入用户数据
private val _userData = MutableLiveData<User>()
val userData: LiveData<User> = _userData
// 通过异步处理加载用户数据
fun loadUserData(userId: String) {
viewModelScope.launch {
try {
_isLoading.value = true
val user = userRepository.getUser(userId)
_userData.value = user
} catch (e: Exception) {
_error.value = e.message
} finally {
_isLoading.value = false
}
}
}
// 自定义的 ViewModel 工厂类,用于创建需要依赖项的 UserViewModel
class Factory(
// 通过构造函数注入 UserRepository 依赖
private val repository: UserRepository
) : ViewModelProvider.Factory { // 实现官方 Factory 接口
/**
* 核心创建方法,由 ViewModelProvider 调用
* @param modelClass 请求的 ViewModel 类型(由系统自动推断)
* @return 创建好的 ViewModel 实例
* @throws IllegalArgumentException 当请求的 ViewModel 类型不匹配时抛出
*/
@Suppress("UNCHECKED_CAST") // 抑制类型转换警告
override fun <T : ViewModel> create(modelClass: Class<T>): T {
// 类型安全检查:确保请求的是 UserViewModel 或其子类
if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
// 通过构造函数创建 ViewModel 并注入依赖
return UserViewModel(repository) as T // 显式类型转换
}
// 防御性编程:拦截非法 ViewModel 类型请求
throw IllegalArgumentException("未知的 ViewModel 类型: ${modelClass.name}")
}
}
}
在Activity/Fragment中
class UserFragment : Fragment() {
private lateinit var userViewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 1. 正确获取 UserRepository 实例
val userRepository = UserRepository()
// 2. 创建工厂实例,传入依赖
val factory = UserViewModel.Factory(userRepository)
// 3. 使用 ViewModelProvider 获取 UserViewModel
userViewModel = ViewModelProvider(
this, // 也可用 requireActivity() 作为 ViewModelStoreOwner
factory
).get(UserViewModel::class.java)
}
// 后续可通过 userViewModel 操作数据
}
4)、其他方式创建ViewModel,实际都是通过ViewModelProvider创建的
通过KTX扩展库方式创建ViewModel,需使用到下面ktx库中的方法:
implementation "androidx.activity:activity-ktx:1.2.2"
implementation "androidx.fragment:fragment-ktx:1.3.3"
我们通过这2个扩展库,使用viewModels就可以非常快速的创建出我们所需要的ViewModel
//Activity中
class MainActivity:AppCompatActivity() {
private val viewModel:MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.mUser.observe(this) {
Log.d("mUser","$it")
}
}
}
//Fragment中
class HomeFragment:Fragment() {
val activityViewModel:MainViewModel by activityViewModels()
//或者
val viewModel:MainViewModel by viewModels()
}
原理大家还是阅读大佬的文章吧。
二、ViewModel解析
``
ViewModel怎么感知生命周期
正如官网介绍的一样,ViewModel也是有生命周期的,如图:
编辑
从官网中我们也了解到ViewModel是通过Lifecycle来感知生命周期的
ViewModel 对象存在的时间范围是获取 ViewModel 时传递给
ViewModelProvider的Lifecycle。ViewModel 将一直留在内存中,直到限定其存在时间范围的Lifecycle永久消。
ViewModel 存储于 ViewModelStore,其存活周期与 ViewModelStoreOwner(Activity/Fragment)保持一致。当 owner 调用 onDestroy 且非配置变更销毁时,系统会清理 ViewModelStore 并调用 ViewModel.onCleared()。
先看ViewModel源码:
public abstract class ViewModel {
// 抑制Lint的"WeakerAccess"警告,说明此protected访问级别是设计需要
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
// 默认空实现,子类按需重写
}
// 声明此方法必须在主线程调用(Android UI线程)
@MainThread
// final修饰符确保子类不能重写此方法,维护框架内部逻辑的稳定性
final void clear() {
....
// 调用模板方法,执行开发者自定义的清理逻辑
onCleared();
}
....
}
在看看ViewModelProvider的源码:
public open class ViewModelProvider(
// 主构造函数参数:ViewModel存储仓库(负责缓存ViewModel实例)
private val store: ViewModelStore,
// ViewModel工厂接口(负责具体实例化ViewModel对象)
private val factory: Factory
) {
// 次构造函数1:通过ViewModelStoreOwner简化初始化
public constructor(
owner: ViewModelStoreOwner // 生命周期所有者(如Activity/Fragment)
) : this(
owner.viewModelStore, // 从owner获取关联的ViewModelStore
defaultFactory(owner) // 根据owner类型选择默认工厂(代码中未展示实现细节)
)
// 次构造函数2:允许自定义Factory的初始化方式
public constructor(
owner: ViewModelStoreOwner,
factory: Factory // 自定义工厂实例
) : this(
owner.viewModelStore, // 从owner获取ViewModelStore
factory // 使用传入的自定义工厂
)
......
}
ViewModelProvider有两个参数,一个是ViewModelStore,另一个是Factory。ViewModelStore:正如其名,就是用来存储ViewModel对象的,通过内部维护一个HashMap来实现对ViewMoel对象的存储与管理工作。而Factory则是工厂接口的,用来实例化ViewModel。
在实例化ViewModel时:
val userViewModel = ViewModelProvider(
this, // 也可用 requireActivity() 作为 ViewModelStoreOwner
factory
).get(UserViewModel::class.java)
}
this就是指当前所在的Activity,也对应着构造函数中的ViewModelStore。
在ViewModelStoreOwner接口中可知:
/**
* 拥有 [ViewModelStore] 的作用域接口(生命周期核心接口).
*
* 实现此接口的类有以下生命周期职责:
* 1. 在配置变更(如屏幕旋转)期间保留持有的 ViewModelStore 实例
* 2. 当该作用域即将被永久销毁时,调用 [ViewModelStore.clear] 清理资源
*
* @see ViewTreeViewModelStoreOwner // 视图树级别的 ViewModel 存储扩展
*/
public interface ViewModelStoreOwner {
/**
* 获取关联的 ViewModel 存储实例(生命周期关键方法)
*
* 生命周期特性:
* - 返回值与实现类的生命周期绑定(如 Activity/Fragment)
* - 当所有者被永久销毁时,内部会触发 ViewModelStore 的清理操作
*/
public val viewModelStore: ViewModelStore // 必须通过实现类提供具体的存储实例
}
而在Activity/Fragment 中实现了ViewModelStoreOwner 接口,并实现了getViewModelStore()方法来得到 ViewModelStore。
ComponnetActivity.kt
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner
...... {
public ViewModelStore getViewModelStore() {
//检查一下当前 mViewModelStore 是否为空
if (mViewModelStore == null) {
//检索一下是否有先前保存的非配置实例数据
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
//从先前保存的非配置实例数据中取出 ViewModelStore
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
//先前没有保存过非配置实例数据,则新建一个 ViewModelStore
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
}
简单概括:在获取 ViewModelStore 这一步中,会先去检索一下是否有先前保存的非配置实例数据,如果有保存,则取出其中的 ViewModelStore; 反之,如果之前没有保存过,则新创建一个 ViewModelStore。
在 ViewModelProvider 的构造函数中通过调用 owner.viewModelStore 方法来获取到 viewModelStore,这一步其实就是取在 Activity | Fragment 的 getViewModelStore() 方法中创建的 ViewModelStore,也就是我们刚刚所介绍的。
viewModelStore 与 Lifecycle关系:
public ComponentActivity() {
......
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
//检查一下是否发生配置变更
if (!isChangingConfigurations()) {
//调用ViewModelStore中的clear()方法来清除其ViewModel
getViewModelStore().clear();
}
}
}
});
......
}
首先我们知道ViewModel是存储在ViewModelStore中的,当我们进入Activity时,会自动创建一个ViewModelStore,当我们在onCreate()方法中调用ViewModelProvider.get()方法时,就会将创建的ViewModel存到该ViewModelStore中。通过Lifecycle来得知Activity的生命周期,当Activity处于销毁时,检查一下是否发生配置变更,如果未发生配置变更,则调用clear()方法来清除ViewModelStore及其保存的ViewModel。
ViewModel不会因Configuration Change(如屏幕旋转)而销毁
当Activity 被杀死并重新创建或资源内存不足导致低优先级的 Activity 被杀死时,需要数据恢复,而数据恢复一般有三种方式。这里我直接放图对比,详细还请自行了解:
编辑
在官方设计之初就倾向于在配置改变时进行数据的恢复。考虑到数据恢复时的效率,官方最终采用了 onRetainNonConfigurationInstance 的方式来恢复 ViewModel 。
在 Androidx 中的 Activity 的最新代码中,官方重写了 onRetainNonConfigurationInstance 方法,在该方法中保存了 ViewModelStore (ViweModelStore 中存储了 ViewModel ),进而也保存了 ViewModel。
/**
* 保留非配置变更相关数据的核心方法(在Activity因配置变更销毁前调用)
*
* 作用:在配置变更(如屏幕旋转)时保存ViewModelStore等数据,
* 保证Activity重建后能恢复这些数据
*
* @return 包含需要保留数据的NonConfigurationInstances对象
*/
public final Object onRetainNonConfigurationInstance() {
// 向后兼容处理:获取子类可能保留的自定义数据(旧版API的兼容逻辑)
Object custom = onRetainCustomNonConfigurationInstance();
// 尝试获取当前ViewModelStore实例
ViewModelStore viewModelStore = mViewModelStore;
// 若当前没有ViewModelStore,尝试从上次保留的实例中获取
if (viewModelStore == null) {
// 通过getLastNonConfigurationInstance获取之前保存的非配置实例
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore; // 复用之前保留的ViewModelStore
}
}
// 数据为空时直接返回null,避免创建无用对象
if (viewModelStore == null && custom == null) {
return null;
}
// 创建新的保留数据容器(当有需要保留的数据时)
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom; // 存入自定义兼容数据
nci.viewModelStore = viewModelStore; // 存入ViewModel存储
return nci;
}
/**
* 非配置实例数据容器(静态内部类避免内存泄漏)
*
* 设计要点:
* 1. 静态类不持有外部引用,防止Activity实例泄漏
* 2. 仅存储必要数据,生命周期与Activity重建过程关联
*/
static final class NonConfigurationInstances {
Object custom; // 兼容旧版API的自定义数据存储位
ViewModelStore viewModelStore; // ViewModel存储核心容器
}
当新的 Activity 重新创建,并调用ViewModelProviders.of(this).get(xxxModel.class) 时,又会在 getViewModelStore() 方法中获取老 Activity 保存的 ViewModelStore。那么也就拿到了 ViewModel。
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,
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
//👇从该对象中获取ViewModelStore
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
最后会先通过 getLastNonConfigurationInstance() 方法来获取先前创建的非配置实例数据,如果先前没有创建过非配置实例数据,则新创建一个,并返回。
Activity.java
NonConfigurationInstances mLastNonConfigurationInstances;
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
final void attach(NonConfigurationInstances lastNonConfigurationInstances ...) {
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
}
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
而在Fragment中,ViewModel 是存储在 FragmentManagerViewModel 中的,而 FragmentManagerViewModel 是存储在宿主 Activity 中的 ViewModelStore 中,又因 Activity 中 ViewModelStore不会因配置改变而销毁,故 Fragment 中 ViewModel 也不会因配置改变而销毁。
编辑
多个 Fragment 可以共享同一 ViewModel
假如我们想 Fragment D 获取 Fragment A 中的数据,那么我们只有在 Activity 中的 ViewModelStore 下添加 ViewModel。当在 Fragment 中使用 ViewModelProvider(requireActivity()) 获取时,所有 Fragment 都访问相同的 ViewModelStore,从而共享同一 ViewModel 实例。
// 1. 首先定义需要共享数据的 ViewModel 类
class SharedViewModel : ViewModel() {
// 使用 LiveData 保持数据,便于观察变化
private val _sharedData = MutableLiveData<String>()
val sharedData: LiveData<String> get() = _sharedData
// 更新共享数据的方法
fun updateData(newData: String) {
_sharedData.value = newData
}
}
// 2. Fragment A(数据生产者)
class FragmentA : Fragment() {
private lateinit var viewModel: SharedViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 通过宿主 Activity 获取共享的 ViewModel
viewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
// 模拟数据更新(例如按钮点击事件)
button.setOnClickListener {
viewModel.updateData("Data from Fragment A")
}
}
}
// 3. Fragment D(数据消费者)
class FragmentD : Fragment() {
private lateinit var viewModel: SharedViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 同样通过宿主 Activity 获取 ViewModel
viewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
// 观察数据变化
viewModel.sharedData.observe(viewLifecycleOwner) { data ->
// 当数据变化时更新 UI
textView.text = "Received: $data"
}
}
}
// 4. 宿主 Activity(容器)
class HostActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_host)
// 添加 FragmentA 和 FragmentD 的逻辑...
}
}
总结
ViewModel 是 Jetpack 架构组件的基石,通过它可以实现更清晰的架构分层、简化配置变更数据恢复,并促进 UI 与业务逻辑的解耦。结合 LiveData能够构建响应式、稳定且易于测试的 Android 应用。