LiveData

156 阅读5分钟

LiveData基本使用

  • 实现数据驱动UI:在界面可见时更新UI

    • MainActivity
     package com.wasbry.myapplication
     ​
     import android.os.Bundle
     import android.widget.TextView
     import androidx.appcompat.app.AppCompatActivity
     import simple.R
     import kotlin.concurrent.thread
     ​
     /**0.实现效果:当界面可见的时候更新UI(TextView里面的值)
      * 1.为什么MainActivity要继承AppComponentActivity
      *      因为通过新建class的形式拿到的,仅仅只是一个kotlin类而已,并非Android中的默认启动类
      * 2.为什么要在onCreate函数中注册眼睛:
      *      这是Google推荐的做饭;
      *      LiveData即是观察者又是被观察者:都是LiveData维护的数据
      *      根据状态机细节,可知只有到mActive为true的时候,LiveData才会工作,在其他地方注册容易出现问题
      * 3.为什么子线程使用的是postValue而主线程中value
      *      postValue最终会调用的setValue中去的;
      *      kotlin中的value就是setValue,其在内部进行了封装的
      * 
      * */
     ​
     ​
     class MainActivity : AppCompatActivity(){
         override fun onCreate(savedInstanceState: Bundle?) {
             super.onCreate(savedInstanceState)
             setContentView(R.layout.activity_main)
     ​
             val textView : TextView = findViewById(R.id.textView)
     ​
             //在宿主的onCreate中去注册眼睛
             MyLiveData.info.observe(this,{
                 //LiveData维护的数据是在眼睛的那个位置更新UI的
                 textView.text = it
             })
     ​
             //触发LiveData中数据的改变
             //通过主线程设置默认值
             MyLiveData.info.value = "默认值"
             //通过子线程设置默认值
             thread {
                 Thread.sleep(3000)
                 MyLiveData.info.postValue("从启动APP到现在已经经过了3秒")
             }
             thread {
                 Thread.sleep(6000)
                 MyLiveData.info.postValue("从启动APP到现在已经经过了6秒")
             }
         }
     }
    
    • MyLiveData
     package com.wasbry.myapplication
     ​
     import androidx.lifecycle.MutableLiveData
     /*
     * 1.为什么要使用单例类:
     *       此案例仅作LiveData基本使用实例,使用单例类保证了全局只需要维护一个MutableLiveData
     * 2.为什么不使用LiveData而使用MutableLiveData
     *       MutableLiveData是LiveData的一个简化版本,内部仅提供了postValue与setValue;
     *       可以看作是轻量级的LiveData,简单--->代码可追逐性强,易于维护
     * 3.为什么要使用懒加载:
     *       这是kotlin内置的特性,在使用时才会加载,避免资源的浪费*/
     object MyLiveData {
         //使用懒加载方式处理,当需要使用到时,才加载这个类;节省资源
         val info : MutableLiveData<String> by lazy { MutableLiveData() }
     }
    

      • LiveData+Service:模拟服务器向客户端推送数据,并且服务器端一直推送数据而客户端只有在可见的时候才更新UI

        • 缺点:

          • 之前的消息被遗失掉了

          • 使用不当会造成异常,应用崩溃(需要保证单例在只能在主或子线程中初始化)

            • 在子线程中已经对单例进行了初始化
            • 但是又在MyLiveData中搞了一个init(这个先走了)
        • 优点:

          • 不会内存泄漏,Handler不会检测生命周期,直接就开始干了
          • 编码简单
        • 特殊说明:

          • 为什么MyLiveData要使用单例类?

            • 因为做到全局单例的效果,在更新UI时,直接将it作为参数传进去就行了(传进去的是观察者)
          • 服务器:由AndroidStudio充当,以打印日志的形式证明"服务器正常工作"

          • 客户端:由编写的应用充当

          • 更新UI:在客户端采用Toast形式作为UI更新证据

        • 编码中发现的问题:

          • 问题一:为什么只能看到info作为Toast更新UI,而看不到info1,并且只留info1,也看不到这个

            • 如果在MyLiveData中添加多条数据

               object MyLiveData {
                   val info : MutableLiveData<String> by lazy { MutableLiveData() }
                   val info2 : MutableLiveData<String> by lazy { MutableLiveData() }
               }
              
            • 在服务中对MyLiveData中两个数据都更新

                 thread {
                           for(x in 1..1000){
                               Thread.sleep(5000)
                               Log.d("MyServer","服务器向客户端推送消息:这是第${x}条消息")
                               MyLiveData.info.postValue("这是客户端收到的第${x}条消息")
                               MyLiveData.info2.postValue("这是测试消息")
                           }
              
            • 在添加眼睛时,选择传入it

               //在宿主中添加眼睛
               MyLiveData.info.observe(MainActivity@this,{
                   Toast.makeText(MainActivity@this,it,Toast.LENGTH_SHORT).show()
               })
              
            • 因为MyLiveData在观察数据的时候是以单例类中的元素为单位的(在添加眼睛哪里就可以看到)

          • 问题二:为什么要在服务中使用子线程更新UI

            1. 服务是主线程的,在服务中只是更新了MyLiveData中的数据而已,UI更新是在MainActivity中的onCreate中进行的
        • 行文思路:

          • 定义单例类,添加类属性(继承自MutableliveData+配合kotlin懒加载)

             object MyLiveData {
                 //只定义一个类属性,使用简化版LiveData---》MutableLiveData并配合懒加载
                 val info  : MutableLiveData<String> by lazy { MutableLiveData() }
             }
            
          • 点击按钮后,启动自定义Service子类,模拟服务器推送消息

             //拿到按钮,添加监听---->启动这个服务
             val button : Button = findViewById(R.id.button)
             button.setOnClickListener {
                 startService(Intent(MainActivity@this,MyService::class.java))
             ​
             }
            
          • 添加眼睛,当界面可见时才执行工作(使用Toast方式证明UI更新)

             //observe:
                 参数1:被观察者(宿主,就是注册眼睛的地方)(LifecycleOwner),
                 参数2:实际的观察者
             //添加眼睛,当界面可见的时候才工作嘛
             //在宿主中添加眼睛
                     MyLiveData.info.observe(MainActivity@this,{
                         Toast.makeText(MainActivity@this,it,Toast.LENGTH_SHORT).show()
                     })
            
          • 在服务中重写方法,使用子线程修改由LiveData维护的数据(模拟服务器将数据推送到客户端)并打印日志,模拟服务器正常工作

             class MyService : Service() {
             ​
                 override fun onBind(intent: Intent): IBinder ?= null
             ​
                 override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
                     //使用子线程进行服务推送
                     thread {
                         for(x in 1..1000){
                             Thread.sleep(5000)//等5秒才推送一次服务
                             Log.d("server","此时服务器仍在推送,这是第${x}条消息了")
                             //在服务中,更新LiveData的数据,模拟服务器将数据拿给客户端
                             MyLiveData.info.postValue("这是服务器推送的第${x}条消息")
                         }
                     }
             ​
                     return super.onStartCommand(intent, flags, startId)
                 }
             }
            
        • 完整代码:

          • MainActivity

             package simple
             ​
             import android.content.Intent
             import android.os.Bundle
             import android.widget.Button
             import android.widget.Toast
             import androidx.appcompat.app.AppCompatActivity
             ​
             //实现微信后台推送效果,服务器一直在推送(借助Service与日志管理),当界面可见才更新UI,界面不可见就不更新UI
             class MainActivity : AppCompatActivity(){
             ​
                 override fun onCreate(savedInstanceState: Bundle?) {
                     super.onCreate(savedInstanceState)
                     setContentView(R.layout.activity_main2)
             ​
                     //拿到按钮,添加监听---->启动这个服务
                     val button : Button = findViewById(R.id.button)
                     button.setOnClickListener {
                         startService(Intent(MainActivity@this,MyService::class.java))
             ​
                     }
                     //添加眼睛,档界面可见的时候才工作嘛
                     MyLiveData.info.observe(this,{
                         Toast.makeText(MainActivity@this,"此时界面可见,消息内容为${it}",Toast.LENGTH_SHORT).show()
                     })
                 }
             }
            
          • MyLiveData

             package simple
             ​
             import androidx.lifecycle.MutableLiveData
             ​
             object MyLiveData {
                 //只定义一个类属性,使用简化版LiveData---》MutableLiveData并配合懒加载
                 val info  : MutableLiveData<String> by lazy { MutableLiveData() }
             }
            
          • MyService

             package simple
             ​
             import android.app.Service
             import android.content.Intent
             import android.os.IBinder
             import android.util.Log
             import kotlin.concurrent.thread
             ​
             class MyService : Service() {
             ​
                 override fun onBind(intent: Intent): IBinder ?= null
             ​
                 override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
                     //使用子线程进行服务推送
                     thread {
                         for(x in 1..1000){
                             Thread.sleep(5000)//等5秒才推送一次服务
                             Log.d("server","此时服务器仍在推送,这是第${x}条消息了")
                             //在服务中,更新LiveData的数据,模拟服务器将数据拿给客户端
                             MyLiveData.info.postValue("这是服务器推送的第${x}条消息")
                         }
                     }
             ​
                     return super.onStartCommand(intent, flags, startId)
                 }
             }
            
  • 证明LiveData数据粘性

    • 行文思路

      • 单例类+Activity1+Activity2
      • 在1--->2,在跳转之前更新MyLiveData数据,在2中进行观察数据,发现修改了,再次订阅居然能收到之前修改的数据
    • 具体实现

      • MyObjective

         package com.wasbry.myapplication
         ​
         import androidx.lifecycle.MutableLiveData
         ​
         object MyLiveData {
             val info : MutableLiveData<String> by lazy { MutableLiveData() }
         }
        
      • MainActivity

         package com.wasbry.myapplication
         ​
         import android.content.Intent
         import androidx.appcompat.app.AppCompatActivity
         import android.os.Bundle
         import android.widget.Button
         ​
         class MainActivity : AppCompatActivity() {
             override fun onCreate(savedInstanceState: Bundle?) {
                 super.onCreate(savedInstanceState)
                 setContentView(R.layout.activity_main)
         ​
                 val button : Button  = findViewById(R.id.button)
                 button.setOnClickListener {
                     MyLiveData.info.value = "这个就是粘性数据"
                     startActivity(Intent(this,MainActivity2::class.java))
                 }
             }
         }
        
      • MainActivity2

         package com.wasbry.myapplication
         ​
         import androidx.appcompat.app.AppCompatActivity
         import android.os.Bundle
         import android.widget.Toast
         ​
         class MainActivity2 : AppCompatActivity() {
             override fun onCreate(savedInstanceState: Bundle?) {
                 super.onCreate(savedInstanceState)
                 setContentView(R.layout.activity_main2)
         ​
                 //添加眼睛监听
                 MyLiveData.info.observe(this,{
                     Toast.makeText(this,it,Toast.LENGTH_LONG).show()
                 })
             }
         }