Android Jetpack ViewModel+LiveData 的 Java 使用指南
一、环境配置
添加依赖
在 build.gradle
中引入 ViewModel 和 LiveData 库:
dependencies {
implementation "androidx.lifecycle:lifecycle-viewmodel:2.6.1"
implementation "androidx.lifecycle:lifecycle-livedata:2.6.1"
}
(Java 项目无需 KTX 扩展,直接使用原生库)
二、创建 ViewModel 与 LiveData
1. 定义 ViewModel
- 继承
ViewModel
类,内部通过MutableLiveData
封装数据,对外暴露只读的LiveData
:
public class UserViewModel extends ViewModel {
private MutableLiveData<String> userName = new MutableLiveData<>();
public LiveData<String> getUserName() {
return userName;
}
public void updateUserName(String name) {
userName.setValue(name); // 主线程更新
}
public void fetchUserAsync() {
new Thread(() -> {
String name = loadFromNetwork(); // 模拟网络请求
userName.postValue(name); // 子线程更新
}).start();
}
}
说明:MutableLiveData
用于内部修改,外部通过 LiveData
观察数据变化
2. ViewModel 初始化
- 在 Activity/Fragment 中通过
ViewModelProvider
获取实例:
public class MainActivity extends AppCompatActivity {
private UserViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(UserViewModel.class);
}
}
注意:ViewModel 实例与 Activity/Fragment 生命周期绑定,配置变化时数据保留
三、观察 LiveData 更新
1. 绑定观察者
- 使用
observe()
方法监听数据变化,仅在活跃生命周期状态(如STARTED
或RESUMED
)触发回调:
viewModel.getUserName().observe(this, new Observer<String>() {
@Override
public void onChanged(String name) {
updateTextView(name); // 更新 UI
}
});
说明:无需手动解除观察,LiveData 自动感知生命周期,避免内存泄漏
2. 处理粘性事件
- 若需避免旧数据重复触发,可通过 Event 包装类标记事件消费状态:
public class Event<T> {
private T content;
private boolean hasBeenHandled = false;
public Event(T content) { this.content = content; }
public T getContentIfNotHandled() {
if (hasBeenHandled) return null;
hasBeenHandled = true;
return content;
}
}
应用场景:弹窗、导航等一次性事件
四、LiveData 的主要子类
LiveData 是一个抽象类,其子类主要用于扩展数据管理能力。常见的子类及工具类包括:
1. MutableLiveData
- 定义:LiveData 的可变子类,允许通过
setValue()
(主线程)或postValue()
(后台线程)修改数据。 - 用途:用于 ViewModel 中存储可变数据,对外暴露不可变的
LiveData
以保护数据安全。
class MyViewModel extends ViewModel {
private MutableLiveData<Integer> counter = new MutableLiveData<>();
public LiveData<Integer> getCounter() { return counter; }
public void increment() { counter.setValue(counter.getValue() + 1); }
}
2. MediatorLiveData
MediatorLiveData 通过 addSource() 实现多数据源监听与合并,结合 Java 语法可灵活应对复杂业务场景。
-
核心概念:
MediatorLiveData 是 LiveData 的子类,允许合并多个 LiveData 源,并在任意源数据变化时触发统一回调。适用于需要多数据源联动更新的场景,例如:- 同时监听网络请求状态和本地数据库数据
- 组合多个 UI 状态(如加载状态 + 数据结果)
-
基础使用步骤:
- 创建 MediatorLiveData 对象
在 ViewModel 中初始化 MediatorLiveData,并定义合并逻辑:
public class MyViewModel extends ViewModel { private MutableLiveData<Integer> sourceA = new MutableLiveData<>(); private MutableLiveData<String> sourceB = new MutableLiveData<>(); private MediatorLiveData<String> mediatorData = new MediatorLiveData<>(); public MediatorLiveData<String> getMediatorData() { return mediatorData; } // 初始化时添加数据源 public MyViewModel() { // 监听 sourceA mediatorData.addSource(sourceA, value -> { String combined = "A:" + value + ", B:" + sourceB.getValue(); mediatorData.setValue(combined); }); // 监听 sourceB mediatorData.addSource(sourceB, value -> { String combined = "A:" + sourceA.getValue() + ", B:" + value; mediatorData.setValue(combined); }); } // 更新源数据 public void updateSourceA(int value) { sourceA.setValue(value); } public void updateSourceB(String value) { sourceB.setValue(value); } }
说明:通过 addSource() 添加监听源,任一源变化时触发合并逻辑
- 在 UI 层观察数据
Activity/Fragment 中观察 MediatorLiveData 的变化:
viewModel.getMediatorData().observe(this, new Observer<String>() { @Override public void onChanged(String combinedData) { updateUI(combinedData); // 统一处理合并后的数据 } });
特点:自动管理生命周期,无需手动移除观察者
- 创建 MediatorLiveData 对象
-
高级场景示例
- 动态切换数据源
根据条件动态添加或移除 LiveData 源:
// 添加新数据源 LiveData<Integer> dynamicSource = new MutableLiveData<>(); mediatorData.addSource(dynamicSource, value -> { mediatorData.setValue("Dynamic:" + value); }); // 移除数据源 mediatorData.removeSource(dynamicSource);
应用场景:分页加载时切换数据源
- 多条件同步更新
当多个数据源需同时满足条件时触发逻辑:
mediatorData.addSource(sourceA, value -> { if (value != null && sourceB.getValue() != null) { mediatorData.setValue(processData(value, sourceB.getValue())); } }); mediatorData.addSource(sourceB, value -> { if (value != null && sourceA.getValue() != null) { mediatorData.setValue(processData(sourceA.getValue(), value)); } });
说明:避免因异步更新导致逻辑不一致
- 动态切换数据源
-
注意事项
- 线程安全
主线程更新数据使用setValue()
,子线程使用postValue()
。 - 避免内存泄漏
动态添加的源需在不需要时通过removeSource()
移除。 - 粘性事件处理
MediatorLiveData 继承自 LiveData,同样具有粘性特性,需通过Event
包装处理一次性事件
- 线程安全
3. Transformations 核心方法
map
转换
将原始LiveData
的值转换为另一种类型,适用于一对一数据映射。
说明:原始数据变更时自动触发转换逻辑MutableLiveData<Integer> sourceLiveData = new MutableLiveData<>(); LiveData<String> mappedLiveData = Transformations.map(sourceLiveData, new Function<Integer, String>() { @Override public String apply(Integer input) { return "Value: " + input; } });
switchMap
转换
根据原始LiveData
的值动态切换数据源,适用于链式依赖数据加载(如根据 ID 查询详情)。
特点:自动取消前一次未完成的异步操作MutableLiveData<String> queryLiveData = new MutableLiveData<>(); LiveData<List<String>> resultLiveData = Transformations.switchMap(queryLiveData, new Function<String, LiveData<List<String>>>() { @Override public LiveData<List<String>> apply(String query) { return repository.search(query); // 返回新的 LiveData } });