Android(Kotlin)Jetpack组件之LiveData使用与API

你若需要时间,还得自己把他造出来。 喝汤能补 (* ^ ▽ ^ *)

前言

  该文章作为学习交流,如有错误欢迎各位大佬指正 (* ^ ▽ ^ *)

概述

  LiveData 是JetPack提供的一种响应式编程组件,可以包含任何类型的数据,并在数据发送变化时通知观察者。

  • LiveData一般与ViewModel一起使用,也可单独用在其他地方。即,在Activity中,我们可以不用手动获取ViewModel中的数据来更新界面,而是当ViewModel中数据改变时,主动通知Activity。
  • LiveData内部使用了Lifecycles组件来自我感知生命周期的变化,从而在Activity销毁时及时释放引用,避免产生内存泄漏
  • 如果界面不可见状态(息屏,或者被其他Activity遮挡),LiveData数据发送了变化,也不会通知观察者,这也是依靠于Lifecycles组件。
  • LiveData数据发送多次变化,当Activity恢复可见状态时,只有最新的那份数据才会通知给观察者,之前的数据都被丢弃了。

使用

依赖库

依赖库添与ViewModel的添加方式一样

基本使用

  1. 创建使用LiveData包含的数据
      MutableLiveData 是一种可变的LiveData,LiveData是一种不可变的LiveData。它们使用的主要方法是:
getValue()  获取LiveData中包含的数据,如果数据为空,则会返回null值
setValue()   给LiveData设置数据
postValue()   在非主线程中,给LiveData设置数据

  在实际使用中,因kotlin语法糖,可以直接通过.value的方式进行获取和赋值。

  1. 通过 LiveData对象的observe()绑定观察者
      任何LiveData对象都可以调用它的observe()方法来观察数据的变化。
observe(LifecycleOwner owner, Observer<? super T> observer)
第一个参数:LifecycleOwner对象
第二个参数:Observer接口,当数据发送变化时,会回调该接口中的onChanged()方法
  • LifecycleOwner 我们可以手动创建,但只要Activity是继承AppCompatActivity的,或者Fragment是继承 androidx.fragment.app.Fragment 的,其本身就是一个LifecycleOwner实例,这时因为AndroidX库已经自动完成了。

  • 扩展:这里的Observer是一个单抽象方法接口参数,但是在低版本的库中不能使用函数式API写法的原因是:第一个参数LifecycleOwner也是一个单抽象方法接口;同时接受两个这样的参数时,要么都使用函数式API方式的,要么都不使用。由于第一个参数时this,所以第二参数无法使用。 但是在lifecycle-extensions:2.2.0中,对该方法进行了扩展,所以第二个参数可以用函数式API方式的

简单例子

  这里以一个小功能进行使用演示,功能是:点击一次按钮,改变界面上的文字,以及重置文本内容的按钮。改变文本的逻辑均在ViewModel中完成。

class MainActivity : AppCompatActivity() {

    lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_live_data)

        viewModel = ViewModelProviders.of(this, MainViewModelFactory("1¥"))
            .get(MainViewModel::class.java)

        btn_change.setOnClickListener {
            viewModel.changeValue()
        }

        btn_reset.setOnClickListener {
            viewModel.reset()
        }
        // 绑定观察者,当数据变化时,自动更新界面
        viewModel.mText.observe(this, Observer { text ->
            tv_content.text = text
        })
    }

}
class MainViewModel(textResume: String) : ViewModel() {

    private var text = MutableLiveData<String>() //创建可变的LiveData类型的数据

    val mText: LiveData<String> //创建不可变的LiveData类型的数据
        get() = text

    private var count = 0
    private val textList = listOf("1¥","2¥","3¥")

    init {
        text.value = textResume
    }

    fun changeValue(){
        count++
        if (count > (textList.size - 1)){
            count = 0
        }

        val str = text.value ?: "0¥"
        text.value = textList[count] + " + " + str
    }

    fun reset(){
        count = 0
        text.value = "1¥"
    }

}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <Button
        android:id="@+id/btn_change"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="change"/>

    <Button
        android:id="@+id/btn_reset"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Reset"/>

</LinearLayout>

扩展:map与switchMap

  当我们的项目变的越来越复杂时候,基本的时候一定是无法满足我们的业务场景的,可能需要进行某些转换操作,而LiveData也提供了通用的map与switchMap两种转换。

扩展:map

  Map函数的使用场景:需要将某个LiveData对象转成任意其他类型的LiveData。

  • Transformations.map(LiveData source , Function<X, Y> mapFunction)函数
    用于将某个类型的LiveData转成任意其他类型的LiveData。
    第一个参数:原始的LiveData对象
    第二参数:一个转换函数,用于编写转换的逻辑
  • 当数据发生变化的时候,map()函数会监听到变化并执行转换函数中的逻辑,然后再将转换后的数据通知给观察者。

大致的代码语法

Transformations.map(原始LiveData对象){  
        转化后的Livedata对象
    }

简单示例:将Book的LiveData对象转为name的LiveData对象。

class Book {
    var name:String = "自由"
    var count:Int = 2
}

    private val nameLiveData = MutableLiveData<Book>()
    val name: LiveData<String> = Transformations.map(nameLiveData){ user->
        user.name
    }

扩展:switchMap

  switchMap函数的使用场景比较固定:某个LiveData对象是调用另外的方法获取的时候。

  • Transformations.switchMap(LiveData source , Function<X, Y> mapFunction)函数
    用于如果某个LiveData对象是调用另外的方法获取的,那么可以将这个LiveData对象转换成另外一个可观察的LiveData对象。
    第一个参数:原始的LiveData对象
    第二参数:一个转换函数,用于编写转换的逻辑,在这个转换函数中必须返回一个LiveData对象
  • 当数据发生变化的时候,switchMap()函数会监听到变化并执行转换函数中的逻辑,然后再将可观察的LiveData对象返回。

  因为在另外的方法中,可能是直接新建的一个对象,如果我们直接在Activity中通过方法获取的LiveData对象的observe函数去监听,这样得到的对象永远都是老的LiveData实例,每次新的LiveData无法观察。

错误代码示范

object Repository {
    fun getBook(name: String): LiveData<Book>{
        val liveData = MutableLiveData<Book>()
        liveData.value = Book(name)
        return liveData
    }
}

class MapViewModel : ViewModel() {
    fun getBookName(name: String): LiveData<Book{
        return Repository.getBook(name)
    }
}

// 在Activity中进行绑定, 这种写法是错误的,无法获取到最新的Book类的信息
        viewModelBook.getBookName("test").observe(this){ book ->
            book.name
        }

正确的方式
代码的效果,点击按钮,生成随机数字,并显示在界面上

class MainActivity : AppCompatActivity() {
    lateinit var viewModelBook: MapViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_live_data)
        
        btn_book.setOnClickListener {
            val nunm = (0..100).random().toString()
            viewModelBook.getBookName(nunm)
        }
        // 绑定观察者,当数据变化时,自动更新界面
        viewModelBook.name2.observe(this, Observer { book ->
            tv_name.text = book.name
        })
class MapViewModel : ViewModel() {
    private val mNameLiveData2 = MutableLiveData<String>()

    val name2: LiveData<Book> = Transformations.switchMap(mNameLiveData2){ book ->
        Repository.getBook(book)
    }

    fun getBookName(name: String) {
        mNameLiveData2.value = name
    }
}
object Repository {

    fun getBook(name: String): LiveData<Book>{
        val liveData = MutableLiveData<Book>()
        liveData.value = Book(name)
        return liveData
    }
}

class Book(name: String) {

    var name:String = name
    var count:Int = 2
}

觉得有帮助的点下赞哟,毕竟三连步骤更多,嘻嘻,谢谢大家的支持(* ^ ▽ ^ *)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值