Android LiveData 全面解析:使用Java构建响应式UI【使用篇】

目录

一、什么是LiveData?

[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() - 数据转换器)

场景1:数据格式化与显示文本转换

场景2:状态判断与布尔转换

场景3:计算派生数据

场景4:数据类型转换

场景5:UI状态控制

场景6:业务逻辑封装

[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的主要优点:

  1. 确保UI与数据状态匹配数据变化时自动通知观察者

  2. 无内存泄漏观察者绑定到Lifecycle对象,生命周期结束时自动清理

  3. 不会因停止的Activity而崩溃如果观察者的生命周期处于非活跃状态,不会接收事件

  4. 自动管理生命周期不需要手动处理生命周期

  5. 数据始终保持最新如果观察者从非活跃变为活跃,会立即收到最新数据

二、基本用法

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;
});

这里提到了两个类LiveDataMutableLiveData ,首先需要明确两者的区别:

特性 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 标记事件状态,确保:

  1. 事件只被消费一次

  2. 避免屏幕旋转等配置变化导致重复触发

  3. 保持LiveData的生命周期感知特性

相关推荐
2501_944525544 小时前
Flutter for OpenHarmony 个人理财管理App实战 - 预算详情页面
android·开发语言·前端·javascript·flutter·ecmascript
清蒸鳜鱼5 小时前
【Mobile Agent——Droidrun】MacOS+Android配置、使用指南
android·macos·mobileagent
2501_915918415 小时前
HTTPS 代理失效,启用双向认证(mTLS)的 iOS 应用网络怎么抓包调试
android·网络·ios·小程序·https·uni-app·iphone
峥嵘life5 小时前
Android EDLA CTS、GTS等各项测试命令汇总
android·学习·elasticsearch
Cobboo5 小时前
i单词上架鸿蒙应用市场之路:一次从 Android 到 HarmonyOS 的完整实战
android·华为·harmonyos
天下·第二5 小时前
达梦数据库适配
android·数据库·adb
定偶5 小时前
MySQL知识点
android·数据结构·数据库·mysql
iwanghang5 小时前
Android Studio 2023.2.1 新建项目 不能选择Java 解决方法
android·ide·android studio
似霰6 小时前
JNI 编程指南10——从内存角度看引用类型
android·jni
南墙上的石头6 小时前
Android端 人工智能模型平台开发实战:模型服务部署与运维平台
android·运维