美文网首页Android开发Android技术知识程序员
Jetpack:在数据变化时如何优雅更新Views数据

Jetpack:在数据变化时如何优雅更新Views数据

作者: 06fd4cf1f427 | 来源:发表于2019-06-29 21:41 被阅读42次

    又到周末好时光,茫茫人海中,与你在掘金相遇,好幸运~请君赏阅本文,相处不易,开门见山,不扯皮。本文讲的是关于Jetpack的架构组件LiveData,LiveData是Lifecycle-aware 组件的一个应用,这意味着LiveData遵守Activity、Fragment和Service等组件的生命周期,在它们生命周期处于活跃状态(CREATEDRESUMED)才进行更新Views。

    使用LiveData步骤

    1. 创建持有某种类型的LiveData对象。通常在ViewModel类来实现该对象。
    2. 定义一个具有onChanged()方法的Observer对象,当LiveData持有数据变化是回调该方法。通常在UI控制器类中实现创建该Observer对象,如Activity或Fragment。
    3. 通过使用observe()方法,将上述的LiveData对象和Observer对象关联在一起。这样Observer对象就与LiveData产生了订阅关系,当LiveData数据发生变化时通知,而在Observer更新数据,所以Observer通常是Activity和Fragment。

    三个步骤就定义了使用LiveData的方式,从步骤可以看出,使用了观察者模式,当LiveData对象持有数据发生变化,会通知对它订阅的所有处于活跃状态的订阅者。而这些订阅者通常是UI控制器,如Activity或Fragment,以能在被通知时,自动去更新Views。

    创建LiveData对象

    LiveData可以包装任何数据,包括集合对象。LiveData通常存储在ViewModel中,并通过getter方法获得。示例:

    class NameViewModel : ViewModel() {
    
        // Create a LiveData with a String
        val currentName: MutableLiveData<String> by lazy {
            MutableLiveData<String>()
        }
    
        // Rest of the ViewModel...
    }
    

    为什么是ViewModel持有LiveData而不是Activity或者Fragment中呢?

    • 这样导致Activity或Fragment代码臃肿,Activity或Fragment一般用来展示数据而不是持有数据。
    • 将LiveData解耦,不和特定的Activity或Fragment绑定在一起。

    创建 观察LiveData 的对象

    有了数据源之后,总需要有观察者来观察数据源,不然数据源就失去了存在的意义。

    那么在哪里观察数据源呢?

    在大多数情况下,在应用组件的onCreate()方法中访问LiveData是个合适的时机。这样可以确保系统不在Activity或Fragment的onResume()方法进行多余的调用;另外这样也确保Activity和Fragment尽早有数据可以进行显示。

    class NameActivity : AppCompatActivity() {
    
        private lateinit var model: NameViewModel
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            // Other code to setup the activity...
    
            // Get the ViewModel.
            model = ViewModelProviders.of(this).get(NameViewModel::class.java)
    
            // Create the observer which updates the UI.
            val nameObserver = Observer<String> { newName ->
                // Update the UI, in this case, a TextView. 
                nameTextView.text = newName
            }
    
            // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
            model.currentName.observe(this, nameObserver)
        }
    }
    

    在讲nameObserver对象传给observe()方法后,存储在LiveData最近的值以参数的形式立即传递到onChange()方法中。当然,如果此时LiveData没有存储值的话,onChange()方法不会被调用。

    更新 LiveData 对象

    LiveData本身没有提供公共方法更新值。如果需要修改LiveData的数据的话,可以通过MutableLiveData来暴露共有方法setValue()postValue()。通常在在ViewModel中使用MutableLiveData,而MutableLiveData暴露不可变的LiveData给Observer。与Observer建立关系后,通过修改LiveData的值从而更新Observer中的视图。

    button.setOnClickListener {
        val anotherName = "GitCode"
        model.currentName.setValue(anotherName)
    }
    

    当单击button时,字符串GitCode会存储到LiveData中,nameTextView的文本也会更新为GitCode。这里通过button的点击来给LiveData设置值,也可以网络或者本地数据库获取数据方式来设置值。

    扩展 LiveData

    可以通过下面的栗子来看看如何扩展LiveData。

    class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
        private val stockManager = StockManager(symbol)
    
        private val listener = { price: BigDecimal ->
            value = price
        }
    
        override fun onActive() {
            stockManager.requestPriceUpdates(listener)
        }
    
        override fun onInactive() {
            stockManager.removeUpdates(listener)
        }
    }
    

    首先建立一个StockLiveData并继承自LiveData,并重写两个重要方法。

    • onActivite() 当有活跃状态的订阅者订阅LiveData时会回调该方法。意味着需要在这里监听数据的变化。
    • onInactive() 当没有活跃状态的订阅者订阅LiveData时会回调该方法。此时没有必要保持StockManage服务象的连接。
    • setValue() 注意到value=price这里是调用了setValue(price)方法,通过该方法更新LiveData的值,进而通知处于活跃状态的订阅者。

    LiveData会认为订阅者的生命周期处于STARTEDRESUMED状态时,该订阅者是活跃的。

    那么如何使用StockLiveData呢?

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        val myPriceListener: LiveData<BigDecimal> = ...
        myPriceListener.observe(this, Observer<BigDecimal> { price: BigDecimal? ->
            // Update the UI.
        })
    }
    

    以Fragment作LifecycleOwner的实例传递到observer()方法中,这样就将Observer绑定到拥有生命周期的拥有者。由于LiveData可以在多个Activity、Fragment和Service中使用,所以可以创建单例模式。

    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 onActivityCreated(savedInstanceState: Bundle?) {
            StockLiveData.get(symbol).observe(this, Observer<BigDecimal> { price: BigDecimal? ->
                // Update the UI.
            })
    
        }
    

    转换 LiveData

    有时候在把数据分发给Observer前,转换存储在LiveData中的值,或者返回一个 基于已有值的LiveData对象 的另外一个LiveData对象。这时候就需要用到 Transformations类来处理了。

    使用Transformations.map()方法可以改变其下游的结果:

    LiveData<User> userLiveData = ...;
    LiveData<String> userName = Transformations.map(userLiveData, user -> {
        user.name + " " + user.lastName
    });
    

    使用Transformations.switchMap()同样可以改变下游的结果,但传递给switchMap()的函数必须返回一个LiveData对象。

    private fun getUser(id: String): LiveData<User> {
      ...
    }
    val userId: LiveData<String> = ...
    val user = Transformations.switchMap(userId) { id -> getUser(id) }
    

    这种转换方式都惰性的,也就是只有Observer来订阅数据的时候,才会进行转换。当在ViewModel需要一个Lifecycle对象,或许这种转换会是很好的解决方案。

    合并多个LiveData 源

    MediatorLiveData是LiveData的子类,它主要用途是用来合并多个LiveData源。当其中一个源数据发生变化是,都会回调订阅MediatorLiveData的观察者的onChanged()方法。例如我们在实际开发中,我们的数据源要么来自服务器,要么来自本地数据库。这里就考虑使用MediatorLiveData。

    更多信息请参阅官网

    总结

    LiveData的入门使用来说还是相对简单的,等到后面讲完Jetpack系列文章,再以一个综合的Demo示例Jetpack涉及到的一些知识点。光看文档,都可以感觉到Android 对设计模式,和MVP模式、MVVM模式设计理念使用得淋漓尽致。所以建议各位同学在代码方面的编写一定要有大局观念,代码规范的还是要有,方便别人就是方便自己。不要为应付功能实现而代码臃肿,后续又不重新架构,一直积累成垃圾码。

    最后

    如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

    最后文末放上一个技术交流群:Android架构设计(185873940)

    PS:群内有许多技术大牛,有任何问题,欢迎广大网友一起来交流,群内还不定期免费分享高阶Android学习视频资料和面试资料包~

    再推荐一篇文章,具体的架构视频,面试专题,学习笔记都在这篇文章中:“寒冬未过”,阿里P9架构分享Android必备技术点,让你offer拿到手软!

    偷偷说一句:群里高手如云,欢迎大家加群和大佬们一起交流讨论啊~

    相关文章

      网友评论

        本文标题:Jetpack:在数据变化时如何优雅更新Views数据

        本文链接:https://www.haomeiwen.com/subject/tjdmcctx.html