Kotlin_LiveData

LiveData 完整笔记

1️⃣ 添加依赖

Kotlin 复制代码
dependencies {
    // LiveData
    implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")
    
    // ViewModel + 协程(配合使用)
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
}

2️⃣ 基本用法

Kotlin 复制代码
// ViewModel
class MainViewModel : ViewModel() {
    // 私有可变,公开只读
    private val _count = MutableLiveData(0)
    val count: LiveData<Int> = _count
    
    fun increment() {
        _count.value = (_count.value ?: 0) + 1
    }
}

// Activity
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        val tvCount = findViewById<TextView>(R.id.tvCount)
        val btnAdd = findViewById<Button>(R.id.btnAdd)
        
        // 观察数据变化
        viewModel.count.observe(this) { count ->
            tvCount.text = "计数:$count"
        }
        
        btnAdd.setOnClickListener {
            viewModel.increment()
        }
    }
}

3️⃣ 两种赋值方式

方法 线程要求 使用场景
setValue() 必须主线程 主线程更新
postValue() 任意线程 子线程更新
Kotlin 复制代码
class MyViewModel : ViewModel() {
    private val _data = MutableLiveData<String>()
    val data: LiveData<String> = _data
    
    fun updateOnMain() {
        _data.value = "主线程数据"  // setValue
    }
    
    fun updateOnBackground() {
        viewModelScope.launch(Dispatchers.IO) {
            val result = fetchData()
            _data.postValue(result)  // postValue
        }
    }
}

4️⃣ 数据转换(Transformations)

Kotlin 复制代码
class MyViewModel : ViewModel() {
    private val _input = MutableLiveData<String>()
    val input: LiveData<String> = _input
    
    // map:数据转换
    val upperCase = Transformations.map(input) { text ->
        text.uppercase()
    }
    
    // switchMap:切换数据源
    private val _userId = MutableLiveData("1")
    val userData = Transformations.switchMap(_userId) { userId ->
        repository.getUser(userId)  // 返回 LiveData
    }
}

5️⃣ 合并多个 LiveData(MediatorLiveData)

Kotlin 复制代码
class MyViewModel : ViewModel() {
    private val _name = MutableLiveData("张三")
    private val _age = MutableLiveData(25)
    
    // 合并两个数据源
    val combined = MediatorLiveData<String>().apply {
        addSource(_name) { name ->
            value = "姓名:$name,年龄:${_age.value}"
        }
        addSource(_age) { age ->
            value = "姓名:${_name.value},年龄:$age"
        }
    }
}

6️⃣ 协程 + LiveData 配合(最常用)

Kotlin 复制代码
class UserViewModel : ViewModel() {
    private val _user = MutableLiveData<User>()
    val user: LiveData<User> = _user
    
    private val _loading = MutableLiveData(false)
    val loading: LiveData<Boolean> = _loading
    
    private val _error = MutableLiveData<String?>()
    val error: LiveData<String?> = _error
    
    fun loadUser(userId: String) {
        viewModelScope.launch {
            _loading.value = true
            _error.value = null
            
            try {
                val result = apiService.getUser(userId)  // suspend 网络请求
                _user.value = result
            } catch (e: Exception) {
                _error.value = e.message
            } finally {
                _loading.value = false
            }
        }
    }
}

7️⃣ UI 层使用

Kotlin 复制代码
class UserActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)
        
        // 观察多个状态
        viewModel.user.observe(this) { user ->
            showUserInfo(user)
        }
        
        viewModel.loading.observe(this) { isLoading ->
            progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
        }
        
        viewModel.error.observe(this) { error ->
            error?.let { toast(it) }
        }
        
        // 加载数据
        viewModel.loadUser("123")
    }
}

8️⃣ LiveData 的坑和解决方案

❌ 问题1:数据粘性(新观察者收到旧数据)
Kotlin 复制代码
// 默认行为:新注册的 Observer 会立即收到最后一条数据
viewModel.data.observe(this) { 
    // 会立刻收到旧数据
}

解决方案使用 StateFlow 或自定义 SingleLiveEvent

Kotlin 复制代码
// 方案1:改用 StateFlow
private val _data = MutableStateFlow<String?>(null)
val data: StateFlow<String?> = _data

// 方案2:自定义 SingleLiveEvent(只通知一次)
class SingleLiveEvent<T> : MutableLiveData<T>() {
    private val pending = AtomicBoolean(false)
    
    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        super.observe(owner) { t ->
            if (pending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        }
    }
    
    override fun setValue(value: T) {
        pending.set(true)
        super.setValue(value)
    }
}
❌ 问题2:内存泄漏
Kotlin 复制代码
// ✅ 正确:使用 lifecycleScope 自动管理
viewModel.data.observe(viewLifecycleOwner) {  // Fragment 用 viewLifecycleOwner
    // Activity 销毁时自动移除
}

// ❌ 错误:手动添加不移除会泄漏
viewModel.data.observeForever { 
    // 必须手动 removeObserver
}

9️⃣ LiveData vs StateFlow 对比

特性 LiveData StateFlow
生命周期感知 ✅ 自动 ❌ 需手动处理
初始值 可选 必须有
数据粘性 ✅ 有 ✅ 有
背压处理 ❌ 不支持 ✅ 支持
数据操作(map/filter) 有限 丰富
配合协程 一般 原生支持
适用场景 简单 UI 状态 复杂数据流
Kotlin 复制代码
// LiveData 写法
private val _state = MutableLiveData(State())
val state: LiveData<State> = _state

// StateFlow 写法
private val _state = MutableStateFlow(State())
val state: StateFlow<State> = _state

🔟 完整实战示例

Kotlin 复制代码
// 1. 数据层
interface ApiService {
    suspend fun getArticles(): List<Article>
}

// 2. Repository
class ArticleRepository(private val api: ApiService) {
    suspend fun fetchArticles(): List<Article> {
        delay(1000)
        return api.getArticles()
    }
}

// 3. ViewModel
class ArticleViewModel : ViewModel() {
    private val repository = ArticleRepository(MockApi())
    
    private val _articles = MutableLiveData<List<Article>>(emptyList())
    val articles: LiveData<List<Article>> = _articles
    
    private val _loading = MutableLiveData(false)
    val loading: LiveData<Boolean> = _loading
    
    private val _error = MutableLiveData<String?>()
    val error: LiveData<String?> = _error
    
    fun loadArticles() {
        viewModelScope.launch {
            _loading.value = true
            _error.value = null
            
            try {
                val data = withContext(Dispatchers.IO) {
                    repository.fetchArticles()
                }
                _articles.value = data
            } catch (e: Exception) {
                _error.value = e.message
            } finally {
                _loading.value = false
            }
        }
    }
}

// 4. Activity
class ArticleActivity : AppCompatActivity() {
    private val viewModel: ArticleViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_article)
        
        viewModel.articles.observe(this) { articles ->
            // 更新 RecyclerView
        }
        
        viewModel.loading.observe(this) { loading ->
            // 显示/隐藏 ProgressBar
        }
        
        viewModel.error.observe(this) { error ->
            error?.let { showError(it) }
        }
        
        viewModel.loadArticles()
    }
}

📝 面试必备口诀

XML 复制代码
LiveData 观察者,生命周期感知强
set 主线程,post 子线程
配合协程拿数据,更新 UI 自动来
注意粘性问题,StateFlow 是升级版
相关推荐
DevRay4 小时前
Java 21虚拟线程深度实战:告别线程池焦虑,百万并发吞吐量直接翻倍(原理+代码+避坑)
java·开发语言
free-elcmacom4 小时前
C++学习<1>程序分区
开发语言·c++
醉颜凉4 小时前
Java 必看:如何彻底避免 HashMap 多线程死循环问题?
java·开发语言
默 语4 小时前
Java新手入门:从零开始安装JDK并配置环境变量
java·开发语言·python·mysql·group by·1024程序员节·数据去重
Brilliantwxx4 小时前
【STM32】 I2C_EEPROM_test 工程(实战篇)
开发语言·stm32·单片机·嵌入式硬件·ecmascript
清水白石0085 小时前
Python 异步编程深度解析:Cancellation 到底是异常还是控制信号?从 asyncio 取消机制到企业级事务设计最佳实践
开发语言·python
kolyle5 小时前
万级 QPS 下的 Token 分发系统架构:从 0 到 1 跑通 AI 时代的“水电煤“
开发语言·人工智能·系统架构·token·qps·极智词元·大模型私有化部署
caoerzhong5 小时前
JeeWMS 开源仓库管理系统 GPL-3.0 合规指南:Java WMS 二次开发前必须弄清的授权边界
java·开发语言·开源·vue
Patrick在香港5 小时前
Python 审计香港开放数据目录:两个端点差 10 倍,只有 9.3% 的资源标了「最后修改时间」
开发语言·数据库·python·数据分析·api·数据治理·开放数据
Sylvia33.5 小时前
火星数据体育API|一站式接入足球篮球电竞等18+项目实时数据
java·开发语言·python·websocket·游戏