JetPack<第二篇>:LiveData & ViewModel

LiveData
1、定义:可观察的数据持有对象
2、优点:
(1)自动更新UI
(2)无内存泄漏
(3)不会引起程序崩溃
(4)无需手动处理生命周期
(5)共享资源 -- 需要继承LiveData并且实现单例
ViewModel
1、定义:用来处理跟UI相关的数据的类
2、优点:
(1)设备信息发生变更数据不会消失
(2)同一个Activity之间的Fragment可以实现数据共享 -- 使用Fragment中的activity提供ViewModel

1、添加依赖

ext.lifecycleVersion = '2.2.0-alpha01'
dependencies {
    // liveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$rootProject.lifecycleVersion"
    // viewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel- ktx:$rootProject.lifecycleVersion"
    implementation "androidx.lifecycle:lifecycle-extensions:$rootProject.lifecycleVersion"
}

2、简单实现

【1】创建ViewModel,并在ViewModel创建MutableLiveData

class UserModel(application: Application) : AndroidViewModel(application) {

    val userLiveData = MutableLiveData<User>() // 创建 LiveData 对象
    private var mApplication: Application? = null

    init {
        userLiveData.postValue(User("张三", 2)) // 通知数据变化了
        mApplication = application
    }

    /**
     * 更新用户信息
     */
    fun updateUserInfo() {
        val user = userLiveData.value?.apply {
            userName = "张三"
            age = (1..100).random()
        }
        userLiveData.value = user // 或改成 userLiveData.postValue(user)

    }
}

【2】User类

data class User(var userName: String, var age: Int)

【3】ViewModelProvider

    val userModel = ViewModelProvider(this@LiveDataActivity).get(UserModel::class.java)

【4】使用 observe 或 observeForever 绑定观察者

    // 使用 observe 会让观察者随着生命周期的活跃而活跃,当生命周期处于活跃状态时LiveData才会更新数据,反之不会更新数据
    // 不需要主动调用 removeObserver(Observer) 移除观察者
    userModel.userLiveData.observe(this@LiveDataActivity, Observer<User> {
        binding.userinfo.text = "大家好,我叫" + it.userName + ",今年" + it.age + "岁了!"
    })

    // 使用 observeForever,会导致观察者一直处于活跃状态
    // 我们需要主动调用 removeObserver(Observer) 移除观察者
    userModel.userLiveData.observeForever {
        binding.userinfo.text = "大家好,我叫" + it.userName + ",今年" + it.age + "岁了!"
    }

【5】触发数据变化

    binding.activityDataButton.setOnClickListener {
         userModel.updateUserInfo()
    }

3、map 和 switchMap

    // 使用map函数,将User类型转成Int类型,Observer的返回值也从User类型转换成Int类型
    // Function 第一个参数表示输入,第二个参数表示输出
    // map 第一个参数表示输入数据,第二个参数Function的返回值是输出数据
    Transformations.map(userModel.userLiveData, Function<User, Int> {
        it.age
    }).observe(this@LiveDataActivity, Observer<Int> {
        binding.userinfo1.text = "大家好,我今年 $it 岁了!"
    })


    // 使用switchMap函数,将User类型转成LiveData<Int>类型,Observer的返回值从User类型转换成Int类型
    // Function 第一个参数表示输入,第二个参数表示输出
    // map 第一个参数表示输入数据,第二个参数Function的返回值是输出数据
    Transformations.switchMap(userModel.userLiveData, Function<User, LiveData<Int>> {
        val userLiveData = MutableLiveData<Int>()
        val age = userModel.userLiveData.value?.age
        userLiveData.postValue(age)
        userLiveData
    }).observe(this@LiveDataActivity, Observer<Int> {
        binding.userinfo.text = "大家好,我今年 $it 岁了!"
    })

其它步骤和 【2】 保持一致。

4、使用 MediatorLiveData 统一管理多个LiveData

【1】创建ViewModel,并在ViewModel创建MediatorLiveData和MutableLiveData

class UserModelByMediator(application: Application) : AndroidViewModel(application) {

    var mediatorLiveData: MediatorLiveData<User> = MediatorLiveData() // 创建 LiveData 集合对象,管理多个LiveData
    val userLiveData1 = MutableLiveData<User>() // 创建 LiveData 对象1
    val userLiveData2 = MutableLiveData<User>() // 创建 LiveData 对象2
    private var mApplication: Application? = null

    init {
        mediatorLiveData.addSource(userLiveData1) {
            mediatorLiveData.value = it
        }
        mediatorLiveData.addSource(userLiveData2) {
            mediatorLiveData.value = it
        }
        userLiveData1.postValue(User("张三", 2)) // 通知数据变化了
        userLiveData2.postValue(User("李四", 3)) // 通知数据变化了
        mApplication = application
    }

    /**
     * 更新用户信息1
     */
    fun updateUserInfo1() {
        val user = userLiveData1.value?.apply {
            userName = "张三"
            age = (1..100).random()
        }
        userLiveData1.value = user // 或改成 userLiveData1.postValue(user)
    }

    /**
     * 更新用户信息1
     */
    fun updateUserInfo2() {
        val user = userLiveData2.value?.apply {
            userName = "李四"
            age = (1..100).random()
        }
        userLiveData2.value = user // 或改成 userLiveData2.postValue(user)
    }
}

【2】user类

和上面一致

【3】ViewModelProvider

val userModelByMediator = ViewModelProvider(this@LiveDataActivity).get(UserModelByMediator::class.java)

【4】绑定观察者

    userModelByMediator.mediatorLiveData.observe(this@LiveDataActivity) {
        when(it.userName) {
            "张三" -> binding.userinfo1.text = "大家好,我叫${it.userName},今年${it.age}岁了!"
            "李四" -> binding.userinfo2.text = "大家好,我叫${it.userName},今年${it.age}岁了!"
        }
    }

【5】触发数据变化

    binding.activityDataButton.setOnClickListener {
        userModelByMediator.updateUserInfo1()
        userModelByMediator.updateUserInfo2()
    }

5、LiveData的单例实现

【1】创建一个单例

class UserLiveData : LiveData<User>() {
    companion object {

        private lateinit var mInstance: UserLiveData

        @JvmStatic
        @MainThread
        fun get(): UserLiveData {
            mInstance = if (::mInstance.isInitialized) {
                mInstance
            } else {
                UserLiveData()
            }
            return mInstance
        }
    }

    override fun onActive() {
        super.onActive()
    }

    override fun onInactive() {
        super.onInactive()
    }

    /**
     * 更新数据
     */
    fun updateUserInfo() {
        var user = get().value
        if (user == null) {
            user = User("", 0)
        }
        user.apply {
            userName = "张三"
            age = (1..100).random()
        }
        value = user // 或者 将 value = user 改成 postValue(user)
    }
}

onActive 和 onInactive 两个方法很重要
onActive:Activity 激活时执行
onInactive:Activity 挂起时执行

【2】绑定观察者

    // LiveData单例实现方式
    UserLiveData.get().observe(this@LiveDataActivity) {
        binding.userinfo1.text = "大家好,我叫" + it.userName + ",今年" + it.age + "岁了!"
    }

【3】触发数据更新

    binding.activityDataButton.setOnClickListener {
        UserLiveData.get().updateUserInfo()
    }

6、ViewModelProvider.Factory使用

没有使用 ViewModelProvider.Factory 的代码:

val userModel = ViewModelProvider(this@LiveDataActivity).get(UserModel::class.java)

使用  ViewModelProvider.Factory 的代码:

val userModel = ViewModelProvider(this@LiveDataActivity, UserModelFactory(application)).get(UserModel::class.java)

不使用 ViewModelProvider.Factory 的两种情况:
【1】当 Model 继承 AndroidViewModel 时,并且 Model 只有 Application 参数时:
class UserModel(application: Application) : AndroidViewModel(application)
【2】当 Model 继承 ViewModel,并且 ViewModel 没有参数时:
class UserModel : ViewModel()

使用 ViewModelProvider.Factory 的情况:
【1】当 Model 继承 AndroidViewModel 时,并且 Model 有除了 Application 参数之外还有其它参数时:
class UserModel(application: Application, age: Int) : AndroidViewModel(application)

class UserModelFactory(private val application: Application) : ViewModelProvider.Factory {
    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(UserModel::class.java)) {
            return UserModel(application, 3) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

【2】当 Model 继承 ViewModel,并且 Model 有参数时:
val userModel = ViewModelProvider(this@LiveDataActivity, UserModelFactory(application)).get(UserModel::class.java)

class UserModelFactory(private val application: Application) : ViewModelProvider.Factory {
    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(UserModel::class.java)) {
            return UserModel(application) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

[本章完...]

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 227,022评论 6 528
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 97,760评论 3 412
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 174,687评论 0 373
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,318评论 1 307
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,112评论 6 405
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,645评论 1 320
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,724评论 3 435
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,885评论 0 285
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,387评论 1 330
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,368评论 3 354
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,506评论 1 365
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,063评论 5 355
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,759评论 3 343
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,150评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,414评论 1 281
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,080评论 3 386
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,494评论 2 370

推荐阅读更多精彩内容