目录
[1.1 LiveData的主要优点:](#1.1 LiveData的主要优点:)
[2.1 创建LiveData对象(LiveData vs MutableLiveData)](#2.1 创建LiveData对象(LiveData vs MutableLiveData))
[2.2 在ViewModel中使用LiveData( setValue 和 postValue )](#2.2 在ViewModel中使用LiveData( setValue 和 postValue ))
[2.2.1 setValue()方法](#2.2.1 setValue()方法)
[2.2.2 postValue()方法](#2.2.2 postValue()方法)
[2.3 在Activity/Fragment中观察LiveData(observe)](#2.3 在Activity/Fragment中观察LiveData(observe))
[2.4 无视生命周期的观察LiveData(observeForever)](#2.4 无视生命周期的观察LiveData(observeForever))
[3.1 LiveData转换 (Transformations)](#3.1 LiveData转换 (Transformations))
[3.1.1 Transformations.map() - 数据转换器](#3.1.1 Transformations.map() - 数据转换器)
[3.1.2 Transformations.switchMap() - 动态数据源切换器](#3.1.2 Transformations.switchMap() - 动态数据源切换器)
[3.2 自定义LiveData](#3.2 自定义LiveData)
[3.3 合并多个LiveData](#3.3 合并多个LiveData)
[3.4 处理事件(避免重复触发)](#3.4 处理事件(避免重复触发))
系列入口导航:Android Jetpack 概述
一、什么是LiveData?
LiveData是Android Jetpack架构组件中的核心成员,它为我们提供了一种响应式的数据持有者,能够感知生命周期并在数据变化时自动更新UI。
LiveData是一个可观察的数据持有者类 ,具有生命周期感知能力 。这意味着LiveData遵循其他应用组件(如Activity、Fragment、Service)的生命周期,确保仅在活跃生命周期状态下更新UI组件。
1.1 LiveData的主要优点:
-
确保UI与数据状态匹配:数据变化时自动通知观察者
-
无内存泄漏:观察者绑定到Lifecycle对象,生命周期结束时自动清理
-
不会因停止的Activity而崩溃:如果观察者的生命周期处于非活跃状态,不会接收事件
-
自动管理生命周期:不需要手动处理生命周期
-
数据始终保持最新:如果观察者从非活跃变为活跃,会立即收到最新数据
二、基本用法
2.1 创建LiveData对象(LiveData vs MutableLiveData)
java
// 创建MutableLiveData(可变的LiveData)
private MutableLiveData<String> userName = new MutableLiveData<>();
private MutableLiveData<Integer> age = new MutableLiveData<>(0); // 初始值:0
private MutableLiveData<List<String>> itemList = new MutableLiveData<>();
// 除了基本结构类型,自定义的也可以
private MutableLiveData<User> user = new MutableLiveData<>();
// ❌ 错误:不能直接实例化LiveData
private LiveData<String> userName = new LiveData<>(); // 编译错误!
// ❌ 错误:LiveData没有带参数的构造函数
private LiveData<String> userName = new LiveData<>("初始值"); // 编译错误!
// ❌ 错误:即使转型也不行
private LiveData<String> userName = (LiveData<String>) new MutableLiveData<>("初始值");
// 虽然能编译,但失去了类型安全性,不推荐
// ✅ 使用Transformations(间接初始化)
private LiveData<String> transformedData = Transformations.map(userName, name -> {
return "Hello, " + name;
});
这里提到了两个类LiveData 和 MutableLiveData ,首先需要明确两者的区别:
| 特性 | LiveData | MutableLiveData |
|---|---|---|
| 可访问性 | public访问只能读取数据 | public访问可读取和修改数据 |
| 数据修改 | 不可修改(immutable) | 可修改(mutable) |
| 能否直接实例化 | ❌ 不能(抽象类) | ✅ 能(具体类) |
| 设置数据的方法 | 无setValue()和postValue()方法 |
有setValue()和postValue()方法 |
| 继承关系 | 父类/基类 | 继承自LiveData的子类 |
| 典型使用场景 | 对外暴露,用于观察数据变化 | 内部使用,用于更新数据 |
| 数据安全性 | 高(数据只读,线程安全) | 相对较低(需要自己控制并发) |
LiveData 通常作为ViewModel的公开属性;而 MutableLiveData 通常在ViewModel内部使用,对外暴露时转型为LiveData
java
class MyViewModel extends ViewModel {
// 内部使用MutableLiveData
private MutableLiveData<String> _userName = new MutableLiveData<>();
// 对外暴露LiveData(只读)
public LiveData<String> getUserName() {
return _userName;// 自动向上转型为LiveData
}
// 修改数据的方法
public void updateUserName(String name) {
_userName.setValue(name);
}
}
2.2 在ViewModel中使用LiveData( setValue 和 postValue)
java
public class UserViewModel extends ViewModel {
private MutableLiveData<String> userName;
private MutableLiveData<Integer> userAge;
private MutableLiveData<Boolean> isLoading;
public UserViewModel() {
userName = new MutableLiveData<>();
userAge = new MutableLiveData<>();
isLoading = new MutableLiveData<>(false);
}
public LiveData<String> getUserName() {
return userName;
}
public LiveData<Integer> getUserAge() {
return userAge;
}
public LiveData<Boolean> getIsLoading() {
return isLoading;
}
public void loadUserData() {
//主线程里更新
isLoading.setValue(true);
// 模拟网络请求
new Thread(() -> {
//在后台线程必须使用postValue
userName.postValue("John Doe");
userAge.postValue(30);
isLoading.postValue(false);
}).start();
}
public void updateUserName(String name) {
userName.setValue(name);
}
}
2.2.1 setValue()方法
必须在主线程调用
立即更新数据并通知观察者
如果多次快速调用,只有最后一次值会被传递
java
// 正确用法 - 在主线程
runOnUiThread(() -> {
liveData.setValue("新数据");
});
// 或在按钮点击等UI事件中
button.setOnClickListener(v -> {
liveData.setValue("按钮被点击");
});
// 错误用法 - 在后台线程会崩溃
new Thread(() -> {
// liveData.setValue("后台数据"); // 会抛出异常
}).start();
// 在主线程使用setValue()
liveData.setValue("A"); // 立即触发观察者
liveData.setValue("B"); // 立即触发观察者
liveData.setValue("C"); // 立即触发观察者
// 观察者会依次收到:A → B → C
2.2.2 postValue()方法
可以在任何线程调用
将值发布到主线程,然后更新数据
如果快速多次调用,可能只有最后一个值被传递
java
// 在后台线程安全更新
new Thread(() -> {
// 执行耗时操作
String result = fetchDataFromNetwork();
// 使用postValue更新UI
liveData.postValue(result);
}).start();
// 在后台线程使用postValue()
new Thread(() -> {
liveData.postValue("A");
liveData.postValue("B");
liveData.postValue("C");
}).start();
// 观察者可能只收到:C(A和B被覆盖)
| 特性 | setValue() | postValue() |
|---|---|---|
| 线程要求 | 必须在主线程调用 | 可在任何线程调用 |
| 执行方式 | 同步立即更新 | 异步延迟更新 |
| 数据更新 | 立即通知所有观察者 | 通过Handler.post()延迟通知 |
| 值覆盖风险 | 无(按顺序执行) | 有(可能跳过中间值) |
| 源码实现 | 直接调用dispatchingValue() | 通过Handler发送到主线程 |
| 适用场景 | 主线程数据更新 | 后台线程数据更新 |
2.3 在Activity/Fragment中观察LiveData(observe)
java
public class MainActivity extends AppCompatActivity {
private UserViewModel userViewModel;
private TextView userNameTextView;
private TextView userAgeTextView;
private ProgressBar loadingProgressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
userNameTextView = findViewById(R.id.user_name_text);
userAgeTextView = findViewById(R.id.user_age_text);
loadingProgressBar = findViewById(R.id.loading_progress);
// 获取ViewModel
userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
// 观察LiveData
setupObservers();
// 加载数据
userViewModel.loadUserData();
// 按钮点击更新数据
findViewById(R.id.update_button).setOnClickListener(v -> {
userViewModel.updateUserName("Jane Smith");
});
}
private void setupObservers() {
// 观察用户名变化
userViewModel.getUserName().observe(this, new Observer<String>() {
@Override
public void onChanged(String name) {
userNameTextView.setText(name);
}
});
// 观察用户年龄变化(使用lambda表达式简化)
userViewModel.getUserAge().observe(this, age -> {
if (age != null) {
userAgeTextView.setText("Age: " + age);
}
});
// 观察加载状态
userViewModel.getIsLoading().observe(this, isLoading -> {
if (isLoading != null && isLoading) {
loadingProgressBar.setVisibility(View.VISIBLE);
} else {
loadingProgressBar.setVisibility(View.GONE);
}
});
}
}
观察者模式在LiveData中的实现 ,建立数据与UI之间的响应式连接,离不开LiveData.observe()。
java
@MainThread
public void observe(
@NonNull LifecycleOwner owner, // 生命周期拥有者
@NonNull Observer<? super T> observer // 观察者回调
)
第一个参数 LifecycleOwner 在前面的章节讲过,AppCompatActivity实现了LifecycleOwner,所以传递 this 就好。
接下来看看第二个参数:
java
// Observer接口定义
public interface Observer<T> {
void onChanged(T t); // 数据变化时调用
}
// 三种创建方式:
// 1. 匿名内部类(传统)
liveData.observe(owner, new Observer<String>() {
@Override
public void onChanged(String value) {
// 处理变化
}
});
// 2. Lambda表达式(Java 8+)
liveData.observe(owner, value -> {
// 处理变化
});
// 3. 方法引用
liveData.observe(owner, this::updateUI);
private void updateUI(String value) {
// 更新UI
}
当识别到数据变化时,就会调用 onChanged 方法。接下来说说不依附生命周期如何来观察
2.4 无视生命周期的观察LiveData(observeForever)
java
@MainThread // ⚠️ 必须在主线程调用!
public void observeForever(@NonNull Observer<? super T> observer)
不需要传递**LifecycleOwner。**接下来看看如何使用的:
java
// observeForever()示例
public class MyService extends Service {
private Observer<String> observer;
@Override
public void onCreate() {
observer = new Observer<String>() {
@Override
public void onChanged(String value) {
// 处理数据
}
};
// 没有LifecycleOwner,使用observeForever
viewModel.data.observeForever(observer);
}
@Override
public void onDestroy() {
// 必须手动移除,否则内存泄漏!
viewModel.data.removeObserver(observer);
super.onDestroy();
}
}
| 特性 | observe() | observeForever() |
|---|---|---|
| 生命周期感知 | ✅ 自动管理 | ❌ 不感知 |
| 自动移除 | ✅ Activity销毁时自动移除 | ❌ 需要手动移除 |
| 使用场景 | UI组件(Activity/Fragment) | 非UI组件、Service、自定义View |
| 内存泄漏风险 | 低 | 高(需要手动管理) |
| 线程要求 | 必须在主线程 | 必须在主线程 |
observeForever() 是一把双刃剑:
-
强大之处:提供完全控制的观察能力,适合复杂场景
-
危险之处:极易导致内存泄漏,需要开发者高度负责
三、高级用法
3.1 LiveData转换 (Transformations)
java
public class ProductViewModel extends ViewModel {
private MutableLiveData<List<Product>> products; //总商品列表
private MutableLiveData<String> searchQuery; //关键词
public ProductViewModel() {
products = new MutableLiveData<>(new ArrayList<>());
searchQuery = new MutableLiveData<>("");
}
// 使用Transformations.map进行数据转换
public LiveData<String> getProductCountText() {
return Transformations.map(products, productList -> {
return "Total products: " + (productList != null ? productList.size() : 0);
});
}
// 使用Transformations.switchMap进行LiveData切换
public LiveData<List<Product>> getFilteredProducts() {
return Transformations.switchMap(searchQuery, query -> {
MutableLiveData<List<Product>> filtered = new MutableLiveData<>();
if (products.getValue() != null) {
List<Product> filteredList = products.getValue().stream()
.filter(product -> product.getName().toLowerCase().contains(query.toLowerCase()))
.collect(Collectors.toList());
filtered.setValue(filteredList);
}
return filtered;
});
}
//更新关键词
public void setSearchQuery(String query) {
searchQuery.setValue(query);
}
public void addProduct(Product product) {
List<Product> currentList = products.getValue();
if (currentList != null) {
currentList.add(product);
products.setValue(currentList); // 注意:要创建新列表或调用setValue触发更新
}
}
}
3.1.1 Transformations.map() - 数据转换器
Transformations.map() 是 LiveData 的数据加工厂,它接收一个 LiveData 输入,通过转换函数处理后,输出一个新的 LiveData。
核心公式 :LiveData<A> → 转换函数 → LiveData<B>
使用场景如下:
场景1:数据格式化与显示文本转换
java
//--------------------------a. 简单格式化---------------------------//
// 原始数据:价格(Double)
private MutableLiveData<Double> productPrice = new MutableLiveData<>(99.99);
// 转换:价格 → 格式化显示文本
public LiveData<String> getFormattedPrice() {
return Transformations.map(productPrice, price -> {
return String.format("¥%.2f", price);
});
}
// 结果:99.99 → "¥99.99"
//--------------------------b. 多字段组合---------------------------//
// 原始数据:用户信息
private MutableLiveData<User> user = new MutableLiveData<>();
// 转换:用户对象 → 显示字符串
public LiveData<String> getUserDisplayName() {
return Transformations.map(user, user -> {
if (user == null) return "未登录";
return user.getLastName() + " " + user.getFirstName();
});
}
// 结果:User("张", "三") → "张 三"
//--------------------------c. 本地化处理---------------------------//
// 原始数据:时间戳
private MutableLiveData<Long> timestamp = new MutableLiveData<>(System.currentTimeMillis());
// 转换:时间戳 → 友好时间显示
public LiveData<String> getFriendlyTime() {
return Transformations.map(timestamp, time -> {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.CHINA);
return sdf.format(new Date(time));
});
}
场景2:状态判断与布尔转换
java
//--------------------------a. 空状态判断---------------------------//
// 原始数据:列表
private MutableLiveData<List<Item>> itemList = new MutableLiveData<>(new ArrayList<>());
// 转换:列表 → 是否为空
public LiveData<Boolean> isEmptyList() {
return Transformations.map(itemList, list -> {
return list == null || list.isEmpty();
});
}
// UI层可以观察这个Boolean来决定显示内容还是空状态提示
//--------------------------b. 条件满足判断---------------------------//
// 原始数据:购物车
private MutableLiveData<Cart> cart = new MutableLiveData<>();
// 转换:购物车 → 是否可以结账
public LiveData<Boolean> canCheckout() {
return Transformations.map(cart, cart -> {
return cart != null &&
!cart.getItems().isEmpty() &&
cart.getTotalPrice() >= 10.0; // 最低消费10元
});
}
// 结账按钮的enable状态可以绑定到这个LiveData
//--------------------------c. 验证状态---------------------------//
// 原始数据:表单字段
private MutableLiveData<String> email = new MutableLiveData<>("");
// 转换:邮箱地址 → 是否有效
public LiveData<Boolean> isEmailValid() {
return Transformations.map(email, email -> {
if (email == null || email.isEmpty()) return false;
return Patterns.EMAIL_ADDRESS.matcher(email).matches();
});
}
场景3:计算派生数据
java
//--------------------------a. 聚合计算---------------------------//
// 原始数据:订单列表
private MutableLiveData<List<Order>> orders = new MutableLiveData<>();
// 转换:订单列表 → 总金额
public LiveData<Double> getTotalAmount() {
return Transformations.map(orders, orderList -> {
return orderList.stream()
.mapToDouble(Order::getAmount)
.sum();
});
}
// 转换:订单列表 → 平均金额
public LiveData<Double> getAverageAmount() {
return Transformations.map(orders, orderList -> {
if (orderList.isEmpty()) return 0.0;
return orderList.stream()
.mapToDouble(Order::getAmount)
.average()
.orElse(0.0);
});
}
//--------------------------b. 统计信息---------------------------//
// 原始数据:用户活动日志
private MutableLiveData<List<ActivityLog>> logs = new MutableLiveData<>();
// 转换:日志列表 → 今日活跃度
public LiveData<Integer> getTodayActivityCount() {
return Transformations.map(logs, logList -> {
LocalDate today = LocalDate.now();
return (int) logList.stream()
.filter(log -> log.getDate().isEqual(today))
.count();
});
}
场景4:数据类型转换
java
//--------------------------a. 集合转换---------------------------//
// 原始数据:完整对象列表
private MutableLiveData<List<Product>> products = new MutableLiveData<>();
// 转换:Product列表 → 名称列表
public LiveData<List<String>> getProductNames() {
return Transformations.map(products, productList -> {
return productList.stream()
.map(Product::getName)
.collect(Collectors.toList());
});
}
// 转换:Product列表 → ID列表
public LiveData<List<String>> getProductIds() {
return Transformations.map(products, productList -> {
return productList.stream()
.map(Product::getId)
.collect(Collectors.toList());
});
}
//--------------------------b. 对象属性提取---------------------------//
// 原始数据:用户对象
private MutableLiveData<User> currentUser = new MutableLiveData<>();
// 转换:User对象 → 用户等级文本
public LiveData<String> getUserLevelText() {
return Transformations.map(currentUser, user -> {
if (user == null) return "普通会员";
switch (user.getLevel()) {
case 1: return "青铜会员";
case 2: return "白银会员";
case 3: return "黄金会员";
case 4: return "钻石会员";
default: return "普通会员";
}
});
}
场景5:UI状态控制
java
//--------------------------a. 可见性控制---------------------------//
// 原始数据:加载状态
private MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);
// 转换:Boolean → View可见性常量
public LiveData<Integer> getLoadingVisibility() {
return Transformations.map(isLoading, loading -> {
return loading ? View.VISIBLE : View.GONE;
});
}
// 在XML中直接使用:
// android:visibility="@{viewModel.loadingVisibility}"
//--------------------------b. 文本颜色变化---------------------------//
// 原始数据:库存数量
private MutableLiveData<Integer> stockCount = new MutableLiveData<>(10);
// 转换:库存数量 → 颜色资源ID
public LiveData<Integer> getStockColor() {
return Transformations.map(stockCount, count -> {
if (count <= 0) return R.color.red; // 红色:无货
if (count <= 5) return R.color.orange; // 橙色:库存紧张
return R.color.green; // 绿色:库存充足
});
}
//--------------------------c. 按钮状态---------------------------//
// 原始数据:是否已点赞
private MutableLiveData<Boolean> isLiked = new MutableLiveData<>(false);
// 转换:Boolean → 点赞图标资源
public LiveData<Integer> getLikeIcon() {
return Transformations.map(isLiked, liked -> {
return liked ? R.drawable.ic_liked : R.drawable.ic_like;
});
}
场景6:业务逻辑封装
java
//--------------------------a. 复杂业务规则---------------------------//
// 原始数据:购物车项
private MutableLiveData<Cart> cart = new MutableLiveData<>();
// 转换:购物车 → 运费计算
public LiveData<Double> getShippingFee() {
return Transformations.map(cart, cart -> {
double totalWeight = cart.getItems().stream()
.mapToDouble(Item::getWeight)
.sum();
double totalPrice = cart.getTotalPrice();
// 复杂运费规则:
// 1. 满100包邮
// 2. 重量超过5kg加收10元
// 3. 偏远地区加收15元
double fee = 8.0; // 基础运费
if (totalPrice >= 100.0) {
fee = 0.0;
} else if (totalWeight > 5.0) {
fee += 10.0;
}
if (isRemoteArea(cart.getAddress())) {
fee += 15.0;
}
return fee;
});
}
//--------------------------b. 数据验证与清洗---------------------------//
// 原始数据:用户输入
private MutableLiveData<String> rawInput = new MutableLiveData<>("");
// 转换:原始输入 → 清洗后数据
public LiveData<String> getCleanedInput() {
return Transformations.map(rawInput, input -> {
if (input == null) return "";
// 1. 去除首尾空格
String cleaned = input.trim();
// 2. 替换多个连续空格为单个空格
cleaned = cleaned.replaceAll("\\s+", " ");
// 3. 特殊字符处理
cleaned = cleaned.replaceAll("[<>]", "");
return cleaned;
});
}
"当你想把一个 LiveData 的值变成另一种形式时,就用 map()"
3.1.2 Transformations.switchMap() - 动态数据源切换器
Transformations.switchMap() 是一个更高级的转换函数,它能够根据一个触发条件动态切换要观察的 LiveData 数据源。
与 map() 的关键区别
map():转换数据内容(A → B)
switchMap():切换数据源 (A → LiveData<B>)切换LiveData源
java
public LiveData<List<Product>> getFilteredProducts() {
return Transformations.switchMap(searchQuery, query -> {
MutableLiveData<List<Product>> filtered = new MutableLiveData<>();
if (products.getValue() != null) {
List<Product> filteredList = products.getValue().stream()
.filter(product -> product.getName().toLowerCase().contains(query.toLowerCase()))
.collect(Collectors.toList());
filtered.setValue(filteredList);
}
return filtered;
});
}
从代码分析工作原理:监听搜索条件变化 --> 动态创建过滤逻辑 --> 自动切换观察目标
第一步:监听搜索条件变化
- searchQuery 是一个 LiveData,保存用户输入的搜索关键词
- 每当用户输入新关键词,searchQuery 的值就会更新
- 每当 searchQuery 变化,就会执行传入的函数
第二步:动态创建过滤逻辑
- 这个函数接收当前的搜索关键词 query 作为参数
- 根据 query 创建并返回一个新的 LiveData(过滤后的产品列表)
第三步:自动切换观察目标
switchMap 会停止观察旧的过滤结果
开始观察新创建的过滤结果 LiveData
将新的过滤结果传递给最终的观察者
3.2 自定义LiveData
java
//创建自定义LiveData
public class CounterLiveData extends LiveData<Integer> {
private int count = 0;
// 增加计数器的方法
public void increment() {
count++;
setValue(count); // 在主线程更新数据
}
// 重置计数器
public void reset() {
count = 0;
setValue(count);
}
}
//在Activity中使用
public class CounterActivity extends AppCompatActivity {
private CounterLiveData counter = new CounterLiveData();
private TextView countText;
private Button addButton;
private Button resetButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_counter);
countText = findViewById(R.id.count_text);
addButton = findViewById(R.id.add_button);
resetButton = findViewById(R.id.reset_button);
// 观察计数器的变化
counter.observe(this, number -> {
// 当counter的值变化时,自动更新UI
countText.setText("计数: " + number);
});
// 点击按钮增加计数
addButton.setOnClickListener(v -> {
counter.increment();
});
// 点击按钮重置计数
resetButton.setOnClickListener(v -> {
counter.reset();
});
}
}
自定义LiveData,我们只需要通过 setValue() 来通知观察者数据发生了改变。
3.3 合并多个LiveData
java
public class ShoppingCartViewModel {
// 两个独立的LiveData:商品数量和单价
private MutableLiveData<Integer> quantity = new MutableLiveData<>(0);
private MutableLiveData<Double> pricePerItem = new MutableLiveData<>(0.0);
// 使用MediatorLiveData合并多个LiveData源
private MediatorLiveData<Double> totalPrice = new MediatorLiveData<>();
public ShoppingCartViewModel() {
// 初始化数量为1,单价为10.0
quantity.setValue(1);
pricePerItem.setValue(10.0);
// 设置MediatorLiveData
setupTotalPrice();
}
private void setupTotalPrice() {
// 监听商品数量变化
totalPrice.addSource(quantity, q -> {
Double price = pricePerItem.getValue();
if (q != null && price != null) {
totalPrice.setValue(q * price);
}
});
// 监听单价变化
totalPrice.addSource(pricePerItem, p -> {
Integer q = quantity.getValue();
if (p != null && q != null) {
totalPrice.setValue(q * p);
}
});
}
// 公开的getter方法
public LiveData<Integer> getQuantity() {
return quantity;
}
public LiveData<Double> getPricePerItem() {
return pricePerItem;
}
public LiveData<Double> getTotalPrice() {
return totalPrice;
}
// 更新数量的方法
public void increaseQuantity() {
Integer current = quantity.getValue();
if (current != null) {
quantity.setValue(current + 1);
}
}
public void decreaseQuantity() {
Integer current = quantity.getValue();
if (current != null && current > 0) {
quantity.setValue(current - 1);
}
}
// 更新单价的方法
public void updatePrice(double newPrice) {
pricePerItem.setValue(newPrice);
}
}
MediatorLiveData继承自 LiveData。可以把它看作是LiveData 的容器或中转站 ,能够监听多个 LiveData 源的变化 ,并将它们合并或转换成一个新的 LiveData。实现了一对多、多对一的复杂关系。
java
// 传统 LiveData:一对一
被观察者 ──→ 观察者
// MediatorLiveData:多对一
被观察者1 ──┐
被观察者2 ──┤
被观察者3 ──┼──→ MediatorLiveData ──→ 观察者
被观察者4 ──┤
被观察者5 ──┘
通过addSource()方法,你可以让一个 MediatorLiveData 同时监听多个 LiveData 的变化。同时可以动态管理,动态地添加或移除数据源,使用removeSource() 移除监听。
java
// 添加源
mediator.addSource(source1, value -> { /*...*/ });
// 移除源(不再监听)
mediator.removeSource(source1);
3.4 处理事件(避免重复触发)
java
public class SingleLiveEvent<T> extends MutableLiveData<T> {
private final AtomicBoolean pending = new AtomicBoolean(false);
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
super.observe(owner, t -> {
if (pending.compareAndSet(true, false)) {
observer.onChanged(t);
}
});
}
@Override
public void setValue(T value) {
pending.set(true);
super.setValue(value);
}
public void call() {
setValue(null);
}
}
// 在ViewModel中使用
public class EventViewModel extends ViewModel {
private SingleLiveEvent<Boolean> showToastEvent = new SingleLiveEvent<>();
private SingleLiveEvent<String> navigationEvent = new SingleLiveEvent<>();
public SingleLiveEvent<Boolean> getShowToastEvent() {
return showToastEvent;
}
public SingleLiveEvent<String> getNavigationEvent() {
return navigationEvent;
}
public void performAction() {
// 处理业务逻辑
// 触发事件(只触发一次)
showToastEvent.setValue(true);
navigationEvent.setValue("next_screen");
}
}
// 在Activity中观察事件
public class EventActivity extends AppCompatActivity {
private EventViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(EventViewModel.class);
viewModel.getShowToastEvent().observe(this, show -> {
if (show != null && show) {
Toast.makeText(this, "Action performed!", Toast.LENGTH_SHORT).show();
}
});
viewModel.getNavigationEvent().observe(this, screen -> {
if (screen != null) {
navigateToScreen(screen);
}
});
}
}
AtomicBoolean****保证线程安全,标记事件是否"待处理":
-
pending = true:有新事件,等待消费
-
pending = false:无新事件或已消费
SingleLiveEvent解决了LiveData在处理一次性事件时的重复触发问题,通过 AtomicBoolean 标记事件状态,确保:
-
事件只被消费一次
-
避免屏幕旋转等配置变化导致重复触发
-
保持LiveData的生命周期感知特性