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 是升级版
相关推荐
sunshine22 girl5 小时前
Java学习七 Java 项目实战1-项目创建和主页面搭建
java·开发语言·学习
熊猫钓鱼>_>5 小时前
Kotlin Multiplatform for OpenHarmony 实战:为 KMPNotifier 实现 OpenHarmony 本地通知引擎
kotlin·ai编程·harmonyos·鸿蒙·openharmony·适配·kmp
菜鸟~noob2336 小时前
【电子战】第三季预告—波武器与电子系统强力毁伤
开发语言·数据库·matlab·电子战
惊讶的猫7 小时前
工具选对之后还不够:商城 Agent 的参数与权限治理
开发语言·python
cmpxr_7 小时前
【MicroPython】如何刷新ESP8266的固件
开发语言·micropython
搭贝7 小时前
上厕所的时间搭好一套系统:AI自动生成实测
开发语言·人工智能·低代码·ai·php·搭贝
智感子7 小时前
智能称重·数据上云:从仪表读数到可追溯的计量凭证
开发语言·php
\光辉岁月/7 小时前
7.javase-面向对象
java·开发语言
weixin_531094698 小时前
heap_4内存管理
java·开发语言·算法
疯狂的维修8 小时前
关于用C#写电机起保停控制
开发语言·c#