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 是升级版
相关推荐
迷迭香yy1 小时前
回测过拟合检测体系从样本内外到组合稳健性评估 IG50免费开源股票数据API接口
服务器·开发语言·数据库·人工智能·python
zmzb01031 小时前
C++课后习题训练记录Day199
开发语言·c++
Zane1994121 小时前
`ClassName()` 只是一步?拆开看 `__new__` 和 `__init__` 各自在干什么
开发语言·python
人邮异步社区1 小时前
如何系统地学习 C++ 语言?
开发语言·c++·学习
知识分享小能手1 小时前
概理论与数理统计学习教程,从入门到精通,在数理统计中应用R软件(21)
开发语言·学习·r语言
Data_Journal1 小时前
Playwright vs Selenium:哪个是最佳无头浏览器
开发语言·python·scrapy·microsoft·编辑器
Chester_19991 小时前
CSP202104C.DHCP服务器
开发语言·程序人生·蓝桥杯
跨境技工小黎1 小时前
LLM蜜罐系统是什么?AI爬虫如何精准绕过?
开发语言·人工智能·python
-今昭-1 小时前
OpenStack Dashboard 部署与图形化创建云主机实战指南
开发语言·php