Android Jetpack系列组件之:LiveData(保姆级教程)

提示:建议新手多查阅官网资料,官网文档永远是第一手学习资料
官网链接:https://developer.android.com/topic/libraries/architecture/livedata?hl=zh-cn


前言

什么是LiveData:及一种可观察的数据存储器类,LiveData具有生命周期感知能力,遵循其他应用组件(如activity、fragment或service)的生命周期
官网的概念进行如下解释:这个LiveData既是一个可以装数据的存储器,也是一个同四大组件的生命周期相互绑定的数据存储组件


一、LiveData 的优势

1.确保界面符合数据状态

LiveData遵循观察者模式,当底层数据发生变化时,LiveData会通知Observe对象,进而去Observe对象中更新UI操作,方便了开发者持续去监听数据的更新状态而更新UI

2.避免发生内存泄漏

观察者会绑定到LifeCycle对象,并关联其生命周期,随多绑定的对象的生命周期的销毁而进行自我清理

3.不会因Activity停止而出现Crash问题

如果观察者的生命周期处于非活跃状态(例如在返回堆栈中的Activity),它便不会接受任何LiveData时间

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

界面组件只是观察相关数据,不会停止或恢复观察,LiveData将自动管理所有这些操作,其在观察时可以感知相关的生命周期的状态变化(同第二条,与LifeCycle对象相绑定,关联其生命周期)

5.数据始终保持最新状态

如果生命周期变为非活跃状态,它会在再次变为活跃状态时接受最新的数据(如曾经在后台的Activity会在返回前台后立刻接受最新的数据)

6.共享资源

可以利用单例模式,扩展LiveData对象以封装相关系统服务,以便在应用中共享它们,LiveData对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察LiveData对象。(之后具体举例说明)

二、如何使用LiveData对象

使用说明

  • 在ViewModel中:创建LiveData的实例,用来存储某种数据类型的数据

  • 在Activity或fragment中:使用onChange() 或 observer() 方法,创建observer对象,用于观察当LiveData对象存储的数据发生变化时界面上的相应处理(更新UI等操作)

  • 大多数情况下,应用组件的onCreate() 方法 是开始观察 LiveData 对象的正确着手点

可以使用 observeForever(Observer) 方法 在没有关联的 LifecycleOwner 对象的情况下注册一个观察者。这种情况下,观察者始终处于活跃状态,因此始终可以收到关于修改的通知,手动调用 removeObserve(Observer) 方法 来移除这些观察者

//ViewModel 层 创建LiveData
class MyViewModel : ViewModel() {
	//实例化 LiveData
	val myLiveData : MutableLiveData<String> by lazy {
		MutableLiveData<String>()
	}
}

//应用组件层 使用&&更新 LiveData
class MyActivity : AppCompatActivity() {
	private val myViewModel : MyViewModel by viewModels()

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)

		//开始观察LiveData
		val myLiveDataObserver = Observer<String> { liveDate ->
			//取数据 用于更新页面UI
			myTextView.text = liveDate 
		}
		//观察 LiveData
		myViewModel.myLiveData.observe(this, myLiveDataObserver)
	}


	//更新LiveData的数据
	private fun updateLiveData() {
		//比如UI层的点击实现,相应更新LiveData的操作
		...
		button.setOnClickLister {
			val changeData = "change Data"
			myViewModel.myLiveData.setValue(changeData)
			//在非主线程使用 postValue() 
		}
	}
}

拓展思路(后续专栏详解):

  • 将LiveData与Room一起使用
  • 将协程与LiveData一起使用

三、应用架构中的LiveData

在架构层使用LiveData与其他组件间的使用规则:

  • LiveData具有生命周期感知能力,遵循Activity 和 Fragment 等实体的生命周期,使用LiveData 在这些生命周期所有者和生命周期不同的其他对象(如ViewModel对象)之间传递数据
  • ViewMode主要负责加载和管理与界面相关的数据,非常适合用于保留LiveData对象,及在ViewModel中创建LiveData对象,并向界面层公开出去
  • 在数据层类中使用LiveData,但注意LiveData不适用于处理异步数据流
  • LiveData与Kotlin Flow的组合使用
//在 Repository 中保留 LiveData 阻塞主线程
class MyRepository {
	fun getUser(): LiveData<List<User>> {
		...
	}

	fun getNewPrimiumUsers(): LiveData<List<User>> {
		return getUser().map { users ->
			
			users.filter { user ->
				user.isPremium
			}
			.filter { user ->
				val lastSyncedTime = dao.getLastSyncedTime()
				user.timeCreated > lastSyncedTime
			}
		}
	}

}

四、扩展LiveData

若观察者的生命周期处于Started或者Resumed状态,LiveData会认为该观察者处于活跃状态

class MyLiveData(symbol: String) : LiveData<BigDecimal>() {
	private val dataManager = DataManager(symbol)

	private val listener = { price: BigDecimal ->
		value = price
	}

	//当LiveData 对象具有活跃观察者时,会调用onActive() 方法
	override fun onActive() {
		dataManager.requestPriceUpdates(listener)
	}
	
	//当LiveData 对象没有任何活跃观察者时,会调用onInactive() 方法
	override fun onInactive() {
		dataManager.removeUpdates(listener)
	}
}

//使用MyLiveData类
public class MyFragment : Fragment() {
	override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
		super.onViewCreated(view, savedInstanceState)
		val myPriceListener: LiveData<BigDecimal> = ...
		myPriceListener.observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
			//update the UI
		}) 
	}
}

单例模式

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val stockManager: StockManager = StockManager(symbol)

    private val listener = { price: BigDecimal ->
        value = price
    }

    override fun onActive() {
        stockManager.requestPriceUpdates(listener)
    }

    override fun onInactive() {
        stockManager.removeUpdates(listener)
    }

    companion object {
        private lateinit var sInstance: StockLiveData

        @MainThread
        fun get(symbol: String): StockLiveData {
            sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
            return sInstance
        }
    }
}

//在Fragment中使用
class MyFragment : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
            // Update the UI.
        })
    }

  • 多个 fragment 和 activity 可以观察 MyPriceListener 实例。仅当一个或多项系统服务可见且处于活跃状态时,LiveData 才会连接到该服务。

五、转换LiveData

需求场景:希望将LiveData 对象分配给观察者之前对存储在其中的值进行更改,或者需要根据另一个实例的值返回不同的LiveData实例,Lifecycle提供Transfromations类来进行相关处理

//对存储在LiveData对象中的值应用函数 并将该结果传播到下游
val userLiveData: LiveData<User> = UserLiveData()
val userName: LiveData<Strring> = userLiveData.map {
	user-> "${user,name} ${user.lastName}"
}

//与map类似,将存储在LiveData对象中的值应用函数,并将结果解封和分派到下游,传递给switchMap()的函数必须返回LiveData对象
private fun getUser(id: String): LiveData<User> {
	...
}
val userId: LiveData<Strng> = ...
val user = userId.switchMap { id-> getUser(id) }
Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐