当前位置: 首页 > news >正文

Android jetpack 之LiveData 结合ViewModel的简单使用

前言

LiveData是一个可以被观察的数据持有类,它可以感知并遵循Activity、Fragment或Service等组件的生命周期。

其中主要包含的方法有:setValue、postValue以及observe;

  • setValue:更新数据必须在主线程
  • postValue:更新数据可以在子线程完成
  • observe:是liveData的监听数据回调

一、使用步骤

1.引入库

implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'

2.创建LiveData

实例化唯一实现了LiveData的MutableLivaData。

 val liveData = MutableLiveData<T>()

 3.赋值LiveData

liveData.setValue(T) //只能在主线程中进行赋值操作

liveData.postValue(T) //可在主线程也可以在子线程

4.观察LiveData事件

livedata.observe(this ) {   

//监听数据有变化时会调用observe

 }

二、实战示例

使用liveData与ViewModel结合使用,在Act中刚改数据在Fragmen中更新数据

1、创建一个ViewModel中包含LiveData

class LiveDataViewModel : ViewModel() {
    val TAG = "LiveDataViewModel"
    private val liveData = MutableLiveData<Int>()

    fun addV(num:Int) {
        liveData.postValue(num)
    }


    fun getLV(): MutableLiveData<Int> {
        return liveData
    }

}

2、在Act中操作VM中的LiveData


class LiveDataActivity : AppCompatActivity() {
// 实例化VM
    val mVm by lazy { ViewModelProvider(this)[LiveDataViewModel::class.java] }
    val tv: TextView by lazy { findViewById(R.id.testViewModelText) }
    val numAct by lazy { MutableLiveData<Int>() }
    var n=0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_viewmodel)
        if (savedInstanceState == null) {
//            加载Fragment
            supportFragmentManager.beginTransaction()
                .replace(R.id.container, LiveDataFragment.newInstance())
                .commitNow()
        }
        tv.text = "获取的数据LVVM:${mVm.getLV().value} ___ ActLive:${numAct.value} __ act_num:${n}"
        findViewById<Button>(R.id.testViewModelAdd).setOnClickListener {
            n++
//            修改VM中的LiveData数据
            numAct.value=n
            mVm.addV(numAct.value?:0)

        }
//        监听数据变化后更新操作
        mVm.getLV().observe(
            this
        ) {
            tv.text = "Act-获取的数据:${it}  ___ ActLive:${numAct.value} __ act_num:${n}"
        }
        findViewById<Button>(R.id.testViewModelGet).visibility = View.GONE
    }
}

3、在Fragment中操作VM中的LiveData


class LiveDataFragment : Fragment() {

    companion object {
        fun newInstance() = LiveDataFragment()
    }
//引入VM
    private val viewModel: LiveDataViewModel by lazy {
        ViewModelProvider(requireActivity())[LiveDataViewModel::class.java]
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return inflater.inflate(R.layout.fragment_main, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        view.findViewById<Button>(R.id.viewModelMessageGet).visibility=View.GONE

//        监听LiveData的数据
        viewModel.getLV().observe(
            requireActivity()
        ) {
            view.findViewById<TextView>(R.id.viewModelMessage).text =
                "LV——Fragment数据:${it}"
        }


    }
}

三、优点

  • 避免内存泄漏:观察者被绑定到组件的生命周期上,当被绑定的组件销毁(destroy)时,观察者会立刻自动清理自身的数据。
  • 实时数据刷新:当组件处于活跃状态或者从不活跃状态到活跃状态时总是能收到最新的数据。

四、LivaData数据倒罐

原因:ViewModel 将数据保留在内存中,这意味着开销要低于从磁盘或网络检索数据。ViewModel 与一个 Activity(或其他某个生命周期所有者)相关联,在配置更改期间保留在内存中,系统会自动将 ViewModel 与发生配置更改后产生的新 Activity 实例相关联。

操作:如、屏幕旋转

官网解释

解决方法

1、反射处理

1、利用反射修改observer.mLastVersion 的值,因为mLastVersion 初始的值为-1,当我们调用了其setValue或者postValue,其vesion会+1;对于每一个观察者的封装ObserverWrapper,其初始version也为-1,也就是说,每一个新注册的观察者,其version为-1;当LiveData设置这个ObserverWrapper的时候,如果LiveData的version大于ObserverWrapper的version,LiveData就会强制把当前value推送给Observer。

参考美团的处理方式

通过重写MutableLivaData


    private class BusMutableLiveData<T> : MutableLiveData<T>() { 

        private val observerMap  = HashMap<Observer<in T>, Observer<in T>>()
        override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
                        super.observe(owner, observer)
            try {
                hook(observer)
            } catch (e: Exception) {
                e.printStackTrace()
            }

        }

        override fun observeForever(observer: Observer<in T>) {
            if (!observerMap.containsKey(observer)) {
                observerMap[observer] = ObserverWrapper(observer)
            }
            super.observeForever(observerMap[observer])
        }

        override fun removeObserver(observer: Observer<in T>) {
            val   realObserver = if (observerMap.containsKey(observer)) {
                observerMap.remove(observer)
            } else {
                observer
            }
            super.removeObserver(realObserver)
        }

        @Throws(Exception::class)
        private fun hook(observer: Observer<in T>) {
            //get wrapper's version
            val classLiveData = LiveData::class.java
            val fieldObservers: Field = classLiveData.getDeclaredField("mObservers")
            fieldObservers.setAccessible(true)
            val objectObservers: Any = fieldObservers.get(this)
            val classObservers: Class<*> = objectObservers.javaClass
            val methodGet = classObservers.getDeclaredMethod("get", Any::class.java)
            methodGet.isAccessible = true
            val objectWrapperEntry = methodGet.invoke(objectObservers, observer)
            var objectWrapper: Any? = null
            if (objectWrapperEntry is Map.Entry<*, *>) {
                objectWrapper = objectWrapperEntry.value
            }
            if (objectWrapper == null) {
                throw NullPointerException("Wrapper can not be bull!")
            }
            val classObserverWrapper: Class<*> = objectWrapper.javaClass.superclass
            val fieldLastVersion: Field = classObserverWrapper.getDeclaredField("mLastVersion")
            fieldLastVersion.setAccessible(true)
            //get livedata's version
            val fieldVersion: Field = classLiveData.getDeclaredField("mVersion")
            fieldVersion.setAccessible(true)
            val objectVersion: Any = fieldVersion.get(this)
            //set wrapper's version
            fieldLastVersion.set(objectWrapper, objectVersion)
        }
    }

2、使用第三方UnPeek-LivaData

implementation 'com.kunminx.arch:unpeek-livedata:7.8.0'

链接地址

 

相关文章:

  • FX-std::list
  • rv1106 PWM控制
  • C语言为例谈数据依赖性
  • Android 7 及以上,Fiddler 抓 https 包
  • 容器适配方法模拟Stack和Queue
  • Ansible命令行模式常用模块使用案例(二)
  • Keil创建stm32f103c8t6标准库
  • c++常用的算术生成算法
  • 深度学习项目--基于DenseNet网络的“乳腺癌图像识别”,准确率090%+,pytorch复现
  • RGV调度算法
  • C++20 `<bit>` 中的整数 2 的幂运算和 `std::bit_cast`:由浅入深的探索
  • 【土地智慧】解码土地利用的基本方针
  • 汉桑科技IPO:潜藏两大风险 公众投资者权益或受损
  • MistralAI挑战DeepSeek:开源模型能否颠覆行业巨头
  • 第6关:牛牛鱼缸-附加题
  • 工作记录 2017-01-09
  • Django系列教程(8)——函数视图及通用类视图
  • 【DevOps】使用Azure DevOps为Azure静态网站配置多阶段部署
  • OpenHarmony 编译运行qemu模拟设备
  • [内网渗透] 红日靶场2
  • 做电影网站被抓/饥饿营销的十大案例
  • wordpress官方模版/sem和seo是什么意思
  • 网站后台编辑器编辑内容无法显示/免费b站推广网站2023
  • 兖州中材建设有限公司网站/营销型网站建设解决方案
  • 好站站网站建设/推广平台的方式有哪些
  • tp5手机网站开发/郑州网络推广代理顾问