一、ViewModel 的核心设计理念
ViewModel 作为 Jetpack 架构组件的核心成员,其设计目标聚焦于解决两大开发痛点:
-
配置变更时的数据丢失(如屏幕旋转导致 Activity 重建)
-
组件间的数据共享(如 Fragment 与 Activity、Fragment 之间通信)
核心特性:
-
生命周期感知:与宿主组件(Activity/Fragment)的生命周期绑定,但不受配置变更影响
-
自动资源释放:宿主组件进入
DESTROYED状态时自动清理 -
轻量级存储:专注于存储与 UI 相关的业务数据,不持有 View 引用
典型应用场景:
java
// 正确示例:ViewModel存储列表数据,配置变更时不丢失
class NewsViewModel extends ViewModel {
private final MutableLiveData<List<Article>> articles = new MutableLiveData<>();
void loadNews() {
// 网络请求或本地加载数据
articles.setValue(repository.fetchArticles());
}
LiveData<List<Article>> getArticles() {
return articles;
}
}
二、ViewModel 的生命周期与宿主绑定机制
2.1 生命周期协同原理
ViewModel 的生命周期与宿主组件的生命周期存在以下关系:
-
创建时机:首次通过
ViewModelProvider获取时创建 -
存活周期:从创建到宿主组件完成
onDestroy()(非配置变更导致的销毁) -
销毁时机:宿主组件的
onCleared()方法调用时释放资源
java
// ViewModel与Activity的生命周期对比
Activity: onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDestroy()
ViewModel: created -> active -> inactive -> onCleared()
2.2 配置变更时的实例保留
ViewModel 之所以能在配置变更时保留实例,核心在于ViewModelStore与FragmentActivity的协同:
-
FragmentActivity在重建时会从ViewModelStore中获取已存在的 ViewModel -
ViewModelStore通过HashMap以ViewModel的类名作为键存储实例 -
新 Activity 通过
NonConfigurationInstances获取前一个 Activity 的ViewModelStore
java
// ViewModelProvider创建逻辑
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
// ViewModelStore核心实现
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
public <T extends ViewModel> T get(String key) {
ViewModel viewModel = mMap.get(key);
if (viewModel == null) {
throw new ViewModelNotFoundException("No ViewModel found with key " + key);
}
return (T) viewModel;
}
public void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
}
三、源码深度解析:从创建到销毁的完整流程
3.1 ViewModelProvider 的工厂模式
ViewModel 的创建通过工厂模式实现,支持自定义实例化逻辑:
-
NewInstanceFactory:默认工厂,通过反射创建 ViewModel 实例
-
AndroidViewModelFactory:支持注入 Application 上下文,用于 AndroidViewModel
-
自定义 Factory:支持构造函数参数注入
java
// 自定义ViewModelFactory示例
public class UserViewModelFactory extends ViewModelProvider.Factory {
private final UserRepository repository;
private final String userId;
public UserViewModelFactory(UserRepository repository, String userId) {
this.repository = repository;
this.userId = userId;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (modelClass.isAssignableFrom(UserViewModel.class)) {
// 带参数的ViewModel构造
return (T) new UserViewModel(repository, userId);
}
throw new IllegalArgumentException("Unknown ViewModel class");
}
}
3.2 ViewModel 的销毁机制
ViewModel 的销毁通过onCleared()方法实现,该方法在以下场景调用:
-
宿主组件完成
onDestroy()且非配置变更 -
ViewModelStore被清除时(如 Activity 调用finish())
java
// ViewModel核心生命周期方法
public class ViewModel {
@CallSuper
protected void onCleared() {
// 子类重写此方法释放资源
}
// 防止内存泄漏的关键:不持有Activity引用
private WeakReference<Activity> activityRef; // 反模式!应避免
}
3.3 ViewModel 与 Lifecycle 的协同
ViewModel 通过LifecycleObserver监听宿主组件的生命周期状态,确保资源合理释放:
java
// ViewModelProvider在Activity中的初始化
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
HasDefaultViewModelProviderFactory,
ViewModelStoreOwner,
HasViewModelStore {
private ViewModelStore mViewModelStore;
@Override
public ViewModelStore getViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// 从NonConfigurationInstances获取已存在的ViewModelStore
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
}
四、高级应用场景与最佳实践
4.1 多 ViewModel 协同与数据共享
java
// 主ViewModel管理多个子ViewModel
class MainViewModel extends ViewModel {
private final UserViewModel userViewModel;
private final NewsViewModel newsViewModel;
public MainViewModel(
ViewModelProvider.Factory factory,
UserRepository userRepo,
NewsRepository newsRepo) {
// 通过工厂创建子ViewModel
userViewModel = new ViewModelProvider(this,
new UserViewModelFactory(userRepo)).get(UserViewModel.class);
newsViewModel = new ViewModelProvider(this,
new NewsViewModelFactory(newsRepo)).get(NewsViewModel.class);
}
// 合并多个LiveData数据
public LiveData<CombinedData> getCombinedData() {
return Transformations.switchMap(userViewModel.getUser(), user -> {
return Transformations.map(newsViewModel.getLatestNews(), news -> {
return new CombinedData(user, news);
});
});
}
}
4.2 ViewModel 与协程的结合
java
// 使用协程构建ViewModel
class PhotoViewModel extends ViewModel {
private final PhotoRepository repository;
private final CoroutineScope scope = new CoroutineScope(
Dispatchers.IO + SupervisorJob() + viewModelJob
);
private val _photos = MutableStateFlow<List<Photo>>(emptyList())
val photos: StateFlow<List<Photo>> get() = _photos
init {
loadPhotos()
}
private fun loadPhotos() {
scope.launch {
try {
val result = repository.getPhotos()
_photos.emit(result)
} catch (e: Exception) {
// 错误处理
}
}
}
override fun onCleared() {
super.onCleared()
scope.cancel() // 销毁时取消协程
}
}
4.3 跨 Fragment 数据共享
java
// Activity中创建共享ViewModel
class SharedViewModel extends ViewModel {
private final MutableLiveData<String> sharedData = new MutableLiveData<>();
void setData(String data) {
sharedData.setValue(data);
}
LiveData<String> getData() {
return sharedData;
}
}
// Fragment1中设置数据
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.setData("Hello from Fragment1");
// Fragment2中观察数据
model.getData().observe(getViewLifecycleOwner(), data -> {
textView.setText(data);
});
五、性能优化与陷阱规避
5.1 避免内存泄漏的最佳实践
错误示例(反模式):
java
class BadViewModel extends ViewModel {
private Activity activity; // 持有Activity引用,导致内存泄漏
public BadViewModel(Activity activity) {
this.activity = activity;
}
}
正确做法:
java
class GoodViewModel extends ViewModel {
// 使用Application上下文(生命周期与应用一致)
private final Application application;
public GoodViewModel(Application application) {
this.application = application;
}
// 通过弱引用持有非UI对象
private WeakReference<DatabaseHelper> dbRef;
}
5.2 异步任务的正确处理
java
// 错误示例:未处理ViewModel销毁时的任务
class ErrorViewModel extends ViewModel {
private Disposable disposable;
void loadData() {
disposable = repository.getData()
.subscribe(data -> { /* 更新UI */ });
}
// 正确做法:在onCleared中取消任务
@Override
protected void onCleared() {
super.onCleared();
if (disposable != null && !disposable.isDisposed()) {
disposable.dispose();
}
}
}
5.3 配置变更时的任务处理
java
// 使用SavedStateHandle保存临时状态
class ConfigChangeViewModel extends ViewModel {
private final SavedStateHandle savedStateHandle;
ConfigChangeViewModel(SavedStateHandle savedStateHandle) {
this.savedStateHandle = savedStateHandle;
}
void loadData() {
// 检查是否已有缓存数据
if (savedStateHandle.contains("data")) {
return;
}
// 发起网络请求
repository.getData().subscribe(data -> {
savedStateHandle.set("data", data); // 保存数据到SavedState
});
}
}
六、ViewModel 与其他架构组件的协同
6.1 ViewModel + LiveData + Room 的经典组合
java
// Room数据库操作返回LiveData
@Dao
interface UserDao {
@Query("SELECT * FROM users")
LiveData<List<User>> getUsers();
@Insert
void insertUser(User user);
}
// ViewModel封装数据操作
class UserViewModel extends ViewModel {
private final UserDao userDao;
UserViewModel(UserDao userDao) {
this.userDao = userDao;
}
LiveData<List<User>> getUsers() {
return userDao.getUsers();
}
void addUser(User user) {
// ViewModel负责业务逻辑,不直接操作UI
new Thread(() -> userDao.insertUser(user)).start();
}
}
6.2 ViewModel 与 WorkManager 的协作
java
// ViewModel启动后台任务
class DownloadViewModel extends ViewModel {
void startDownload(String url) {
WorkRequest request = new OneTimeWorkRequest.Builder(DownloadWorker.class)
.setInputData(new Data.Builder()
.putString("url", url)
.build())
.build();
WorkManager.getInstance().enqueue(request);
}
LiveData<DownloadStatus> getDownloadStatus() {
// 通过WorkManager获取任务状态
return WorkManager.getInstance().getWorkInfoByIdLiveData(request.getId());
}
}
七、ViewModel 的局限性与扩展方案
7.1 局限性
- 不适合处理长时间运行的后台任务(如 Service)
- 不支持跨进程数据共享(需配合 ContentProvider)
- 构造函数依赖注入需手动处理(推荐使用 Hilt 等依赖注入框架)
7.2 与其他框架的融合
java
// 使用Hilt实现ViewModel依赖注入
@HiltViewModel
class UserViewModel @Inject constructor(
private val userRepository: UserRepository,
private val localDataSource: LocalDataSource
) : ViewModel() {
fun getUserData(): LiveData<User> {
return userRepository.getUserData(localDataSource.getCachedId());
}
}
八、总结:ViewModel 在现代 Android 架构中的定位
ViewModel 通过生命周期感知和配置变更免疫两大特性,成为 MVVM 架构中连接 Model 与 View 的桥梁。其核心设计遵循以下原则:
-
关注点分离:专注于业务逻辑,不涉及 UI 操作
-
声明式编程:通过 LiveData 等响应式组件暴露数据
-
生命周期安全:自动管理资源释放,杜绝内存泄漏
在实际开发中,合理使用 ViewModel 能显著提升代码的可维护性和可测试性,尤其适合复杂界面和需要处理配置变更的场景。结合协程、Hilt 等现代技术,ViewModel 将在 Android 架构演进中持续发挥核心作用。