Jetpack:LiveData使用指南,实现原理详细解析!,阿里Android开发手册

  • LiveData.observeForever()与observe()的区别

    • observe
  • observeForever

  • ViewModel+LiveData实现Fragment间通信

    • 原理
  • 具体实现

  • 结果

  • LiveData原理详解

  • 其他杂谈

    • postValue使用相关注意点
  • 简单总结


简介

=====================================================================

LiveData是一个可被观察的数据容器类。可以将LiveData理解为一个数据的容器,它将数据包装起来,使数据成为被观察者,当该数据发生变化时,观察者能够获得通知。

LiveData和ViewModel的关系


本篇文章内容结合使用了ViewModel,如果不了解ViewModel可移步查看Jetpack:ViewModel使用指南,实现原理详细解析!

ViewModel用于存放页面所需要的各种数据,不仅如此,我们还可以在其中放一些与数据相关的业务逻辑。我们可以在ViewModel中进行数据的加工、获取等操作。因此,ViewModel的数据可能会随着业务的变化而变化。

但是对于页面来说,它并不需要关心ViewModel中的业务变化,只关心数据在变化之后可以获取到通知并进行更新。LiveData就可以做到,在ViewModel中的数据发生变化时通知页面。所以,LiveData通常被放在ViewModel中用来包装那些需要被界面观察的数据。

依赖


buildscript {

//android lifecycle version

ext.lifecycle_version = “2.3.0”

}

// ViewModel

implementation “androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version”

// LiveData

implementation “androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version”

LiveData的基本使用

================================================================================

写一个计时器,每隔一秒钟是同LiveData通知主界面刷新。

1.LiveData是一个抽象类,不能直接使用。通常我们使用的是它的直接子类MutableLiveData。使用MutableLiveData将要发送的num包装起来,异步线程的话使用postValue设置要发送的值。ViewModel的代码如下所示:

class TimingWithLiveDataViewModel : ViewModel() {

/**

  • 当前的计数 初始值为0

*/

val currentNum: MutableLiveData = MutableLiveData(0)

/**

  • job

*/

var job: Job? = null

/**

  • 开始计数

*/

fun startTiming() {

if (job == null) {

job = viewModelScope.launch(Dispatchers.Default) {

while (true) {

delay(1000)

var value = currentNum.value ?: 0

currentNum.postValue(++value)

}

}

}

}

override fun onCleared() {

super.onCleared()

job?.cancel()

job = null

}

}

2.在activity中,使用observe,设置监听回调。完成页面与ViewModel之间的通信。

class TimingWithLiveDataActivity : AppCompatActivity() {

lateinit var binding: ActivityCommonBinding

lateinit var timingWithLiveDataViewModel: TimingWithLiveDataViewModel

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

initView()

initViewModel()

initData()

}

@SuppressLint(“SetTextI18n”)

private fun initView() {

binding = ActivityCommonBinding.inflate(layoutInflater)

setContentView(binding.root)

binding.btDoAny.visibility = View.VISIBLE

binding.btDoAny.text = “reset”

}

private fun initViewModel() {

timingWithLiveDataViewModel =

ViewModelProvider(this)[TimingWithLiveDataViewModel::class.java]

}

@SuppressLint(“SetTextI18n”)

private fun initData() {

//开始倒计时

timingWithLiveDataViewModel.startTiming()

timingWithLiveDataViewModel.currentNum.observe(this, { currentNum ->

//观察到数据变化 更新UI

binding.tvContent.text = “currentNum:$currentNum”

})

//通过LiveData重置数据

binding.btDoAny.setOnClickListener {

timingWithLiveDataViewModel.currentNum.value = 0

}

}

}

通过LiveData.observe()方法对LiveData所包装的数据进行观察。当我们希望修改LiveData所包装的数据时,则可以通过LiveData.postValue()/LiveData.setValue()方法来完成。postValue()方法用在非UI线程中,若在UI线程中,则使用setValue()方法。注意:setValue()和observe()不可以使用在非UI线程之中,否则会抛异常,稍后的源码分析会讲到,先贴上抛异常的代码。

static void assertMainThread(String methodName) {

if (!ArchTaskExecutor.getInstance().isMainThread()) {

throw new IllegalStateException(“Cannot invoke " + methodName + " on a background thread”);

}

}

LiveData.observeForever()与observe()的区别

=========================================================================================================

LiveData还有一个名为observeForever()的方法,使用起来与observe()没有太大差别。它们的区别主要生命周期的监听。

observe


会在lifecycle大于等于Started的时候才为激活状态(可以观察到数据变化给回调),在lifecycle为Destroy的时候会自动调用removeObserver()移除观察者。可以有效的防止内存泄漏,程序异常。

observeForever


无论页面处于什么状态,observeForever()都能收到通知。因此,在用完之后,一定要记得调用removeObserver()方法来移除观察者,否则会造成了内存泄漏。

ViewModel+LiveData实现Fragment间通信

==================================================================================================

原理


ViewModel时储存在ViewModelStore里面的,而ViewModelStore是通过getViewModelStore获取的,我们只需要传入activity的ViewModelStoreOwner接口,那么多个fragment之间就获取的是同一个ViewModelStore了,那这样就能获取到同一个ViewModel了,然后利用LiveData观察者的特性即可实现数据之间的共享了。

具体实现


1.定义ShareDataViewModel利用LiveData包装progress字段。

class ShareDataViewModel : ViewModel() {

val progress: MutableLiveData by lazy(LazyThreadSafetyMode.NONE) {

MutableLiveData(0)

}

}

2.定义相关布局文件,将两个fragment平放在一个布局里面。

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”

android:orientation=“vertical”

android:layout_width=“match_parent”

android:layout_height=“match_parent”>

<FrameLayout

android:id=“@+id/fl_id_1”

android:layout_width=“match_parent”

android:layout_height=“0dp”

android:layout_weight=“1”/>

<View

android:layout_width=“match_parent”

android:layout_height=“1dp”

android:background=“@color/design_default_color_primary”/>

<FrameLayout

android:id=“@+id/fl_id_2”

android:layout_width=“match_parent”

android:layout_height=“0dp”

android:layout_weight=“1”/>

定义fragment布局文件

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”

android:layout_width=“match_parent”

android:layout_height=“match_parent”>

<TextView

android:id=“@+id/tv_content”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_centerInParent=“true”

android:layout_above=“@id/seekbar”

android:text=“@string/app_name”/>

<SeekBar

android:id=“@+id/seekbar”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:layout_marginTop=“20dp”

android:max=“100”

android:layout_centerInParent=“true”/>

3.编写Fragment代码,实现具体的通信,注意ViewModelProvider传入activity

class SeekBarFragment

@JvmOverloads

constructor(private val text: String = “”) : Fragment() {

override fun onCreateView(

inflater: LayoutInflater,

container: ViewGroup?,

savedInstanceState: Bundle?

): View {

return FragmentSeekbarBinding.inflate(layoutInflater).let {

fragmentSeekBarBinding ->

fragmentSeekBarBinding.tvContent.text = this.text

//注意这里使用activity,因为如果想共享数据需要使用同一个ViewModelStore。所以都是用activity的。

val liveDataProgress = ViewModelProvider(activity!!)[ShareDataViewModel::class.java].progress

//通过observe观察ViewModel中字段数据的变化,并在变化时得到通知

liveDataProgress.observe(this){

progress->

fragmentSeekBarBinding.seekbar.progress = progress

}

fragmentSeekBarBinding.seekbar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {

override fun onProgressChanged(

seekBar: SeekBar?,

progress: Int,

fromUser: Boolean

) {

//当用户操作SeekBar时,更新ViewModel中的数据

liveDataProgress.value = progress

}

override fun onStartTrackingTouch(seekBar: SeekBar?) {

}

override fun onStopTrackingTouch(seekBar: SeekBar?) {

}

})

fragmentSeekBarBinding.root

}

}

}

5.编写activity代码,将fragment实例化,并放入activity中。

class ShareFragmentActivity : AppCompatActivity(){

lateinit var binding: ActivityFragmentContentBinding

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

initView()

}

private fun initView(){

binding = ActivityFragmentContentBinding.inflate(layoutInflater)

setContentView(binding.root)

supportFragmentManager.beginTransaction().add(binding.flId1.id,SeekBarFragment(“One-Fragment”)).commit()

supportFragmentManager.beginTransaction().add(binding.flId2.id,SeekBarFragment(“Two-Fragment”)).commit()

}

}

结果


无论滑动上面的SeekBar还是下面的SeekBar,另一个Fragment的SeekBar一定会跟着滑动。在滑动SeekBar时,通过LiveData.setValue()方法,修改了ViewModel中LiveData包装的数据(progress字段)。由于Fragment通过LiveData.observe()方法监听了数据的变化,因此progress字段被修改后,Fragment能够第一时间收到通知并更新UI。

在这里插入图片描述

LiveData原理详解

===============================================================================

LiveData.observe的源码:

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {

assertMainThread(“observe”);

if (owner.getLifecycle().getCurrentState() == DESTROYED) {

// ignore

return;

}

LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);

ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);

if (existing != null && !existing.isAttachedTo(owner)) {

throw new IllegalArgumentException(“Cannot add the same observer”

  • " with different lifecycles");

}

if (existing != null) {

return;

}

owner.getLifecycle().addObserver(wrapper);

}

上面说到过observe只能在主线程使用,observe里面第一步就用assertMainThread进行了主线程判断,如果当前运行的不是主线程的话,直接就抛异常了(assertMainThread内部实现在上面贴过了)。同理LiveData的observeForever、removeObserver、removeObservers与setValue,也是一样的道理,在方法的第一步就用assertMainThread进行了判断,只能在主线程使用,否则就抛异常

接下来看和生命周期相关的内容,如果当前生命周期是DESTROYED的话就直接return。LifecycleOwner传入到了LifecycleBoundObserver里面进行了包装,将包装后的对象和observer传入了mObservers(一个key value集合)进行保存,最后注册生命周期。所以看看LifecycleBoundObserver做了什么操作

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {

@NonNull

final LifecycleOwner mOwner;

LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {

super(observer);

mOwner = owner;

}

@Override

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

我搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节

img

在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

我搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节

img

在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值