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
- 服务是主线程的,在服务中只是更新了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() }) } }
-
-