Android Jetpack组件库LiveData源码深入探究

目录
  • 前言
  • 一、LiveData
  • 二、使用案例
  • 三、LiveData 实现原理
  • 四、LiveData 相关源码
  • 五、LiveData分发问题

Android Jetpack之ViewModel、LiveData

Android Jetpack之LifeCycle

Android Jetpack之DataBinding+ViewModel+LiveData+Room

前言

Jetpack是一个由多个技术库组成的套件,可帮助开发者遵循最佳做法,减少样板代码并编写可在各种Android版本和设备中一致运行的代码,让开发者精力集中编写重要的代码。

一、LiveData

LiveData 是一种持有可被观察的数据存储类。和其他可被观察的类不同的是LiveData 可以在 Activity ,fragment 或者 service 生命周期发生改变时通知更新。LiveData 已经是必不可少的一环了,例如 MVVM 以及 MVI 开发模式中,都用到了 LiveData。

(注意️源码是Java代码)

LIveData 的优势:

  • 确保界面符合数据状态:数据发生变化时,就会通知观察者。我们可以再观察者回调中更新界面,这样就无需在数据改变后手动更新界面了。
  • 没有内存泄漏,因为关联了生命周期,页面销毁后会进行自我清理。
  • 不会因为Activity 停止而导致崩溃,页面处于非活跃状态时,他不会接收到任何 LiveData 事件。
  • 共享资源,可以使用单例模式扩展 LiveData 对象,以便在应用中共享他们。
  • 屏幕翻转数据状态保留
  • 不再需要手动处理生命周期
  • 数据始终保持最新状态

二、使用案例

LiveData 是一种可用于任何数据的封装容器,通常 LiveData 存储在 ViewModel 对象中。

class JokesDetailViewModel : ViewModel() {
    //创建 LiveData
    private val _state by lazy { MutableLiveData<JokesUIState>() }
    val state : LiveData<JokesUIState> = _state
    private fun loadChildComment(page: Int, commentId: Int, parentPos: Int, curPos: Int) {
        viewModelScope.launch {
            launchHttp {
                jokesApi.jokesCommentListItem(commentId, page)//请求数据
            }.toData {
                //通知观察者
                _state.value = JokesUIState.LoadMoreChildComment(it.data, parentPos, curPos)
            }
        }
     }
}
//观察 LiveData
viewModel.state.observe(this, Observer {
    //更新 UI
})

三、LiveData 实现原理

  • Observer:观察者接口;
  • LiveData:发送已经添加观察的逻辑都在其中;
  • ObserverWrapper:抽象的观察者包装类;
  • LifecycleBoundleObserver:继承 ObserverWrapper;
  • LifecycleEventObserver,生命周期相关回调;

observe(this,Observer{})

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        //如果已经销毁,直接退出
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            return;
        }
        //包装类(下面源码)
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        //判断是否已添加
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
           //....
        }
        //之前添加过
        if (existing != null) {
            return;
        }
        //注册 lifecycle,生命周期改变时会回调
        owner.getLifecycle().addObserver(wrapper);
    }

LifecycleBoundObserver(owner, observer)

class LifecycleBoundObserver
                extends ObserverWrapper implements LifecycleEventObserver {
    @NonNull
    final LifecycleOwner mOwner;
    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
        super(observer);
        mOwner = owner;
    }
    //当前状态大于或者等于 STARTED 返回 true
    @Override
    boolean shouldBeActive() {
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }
    //生命周期相关回调
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
        //如果已经销毁,移除观察者
        if (currentState == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        Lifecycle.State prevState = null;
       //循环更新状态
        while (prevState != currentState) {
            prevState = currentState;
            //修改活跃状态
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }
    @Override
    boolean isAttachedTo(LifecycleOwner owner) {
        return mOwner == owner;
    }
    @Override
    void detachObserver() {
        mOwner.getLifecycle().removeObserver(this);
    }
}
  • 继承 ObserverWrapper 的包装类--activeStateChanged(..)方法。
  • 实现LifecycleEventObserver 接口--onStateChanged(..)方法。

ObserverWrapper.activeStateChanged()

void activeStateChanged(boolean newActive) {
  //如果等于之前状态
  if (newActive == mActive) {
    return;
  }
  mActive = newActive;
  //活跃 +1,不活跃 -1 (下面有源码)
  changeActiveCounter(mActive ? 1 : -1);
  //如果状态变成了活跃状态,直接调用 dispatchingValue,传入当前的观察者
  if (mActive) {
    dispatchingValue(this); //(下面有源码)
  }
}
//修改活跃数量
void changeActiveCounter(int change) {
    int previousActiveCount = mActiveCount;//之前活跃的数量
    mActiveCount += change;//总活跃数量
    if (mChangingActiveState) {//如果正在更改,退出
        return;
    }
    mChangingActiveState = true;//更改中
    try {
        while (previousActiveCount != mActiveCount) {
            boolean needToCallActive = previousActiveCount == 0 && mActiveCount > 0;
            boolean needToCallInactive = previousActiveCount > 0 && mActiveCount == 0;
            previousActiveCount = mActiveCount;
            //如果当前是第一个激活的,调用 onActive
            if (needToCallActive) {
                onActive();//当活动的观察者从0 变成 1的时候调用
            //如果没有激活的为 0 ,调用 onInactive
            } else if (needToCallInactive) {
                onInactive();//活跃的观察者变成 0 时调用
            }
        }
    } finally {
        mChangingActiveState = false;
    }
}
//分发数据
void dispatchingValue(@Nullable ObserverWrapper initiator) {
  if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;  //如果正在分发,退出
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        //如果观察者不为空
        if (initiator != null) {
            considerNotify(initiator);//对数据进行派发,通知观察者
            initiator = null;
        } else {
          //如果为空,遍历所有的观察者,将数据发送给所有观察者
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}
//数据进行派发,通知观察者
private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    //再次进行判断,如果不活跃,则会更新状态,然后退出
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    //观察者版本是否低于当前的版本
    //初始值 mLastVersion 为 -1,mVersion 为 0
    //小于等于表示没有需要更新的数据
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    //更新版本
    observer.mLastVersion = mVersion;
    //通知观察者
    observer.mObserver.onChanged((T) mData);
}

梳理流程:

1、通过 observe 添加一个观察者,这个观察者会被 LifecycleBoundObserver 进行一个封装,LifecycleBoundObserver继承 ObserverWrapper,并且实现了 LifecycleEventObserver。之后就会将观察添加到 Observers 中,最后注册页面生命周期的 observer。

2、当生命周期发生变化后,就会回调到 LifecycleBoundObserve 中的 onStateChanged 方法中。如果生命周期是销毁的,就会移除观察者,如果不是就会循环更新当前状态。

3、在更新状态的时候就会判断是否为活跃状态,如果是活跃状态就会进行分发,分发的时候如果观察者为 null ,就会遍历所有的观察者进行分发,否则就分发传入的观察者。

4、最后会再次判断活跃状态,已经判断观察者版本是否低于当前版本,如果都满足,就会更新观察者。

onActive 与 onInactive

如果观察者的生命周期处于 STARTED 或者 RESUMED 状态,LiveData 就会认为观察者处于活跃状态。

例如,下载数据需要在活跃状态下进行,或者需要实时的监听后台数据,就可以重新下面方法,并完成对应逻辑。

class StockLiveData(url: String) : LiveData<String>() {
    private val downloadManager = DownloadManager(url)
    override fun onActive() {
      if(!downloadManager.isStart()){
          downloadManager.start()
      }
    }
    override fun onInactive() {
         downloadManager.stop()
    }
}

当具有活跃的观察者时,就会调用 onActive 方法。

当没有任何活跃的观察者时,就会调用 onInactive 方法。

当然这只是我想到的场景,开发中可以根据不同的业务场景做出不同的判断。

四、LiveData 相关源码

MutableLiveData

由于 LiveData 的发送数据方法是 protected 修饰私有受保护的,所以不能直接调用。因此使用MutableLiveData 继承 LiveData 将发送数据的方法改为了 public。

public class MutableLiveData<T> extends LiveData<T> {
    /**
     * Creates a MutableLiveData initialized with the given {@code value}.
     */
    public MutableLiveData(T value) {
        super(value);
    }
    /**
     * Creates a MutableLiveData with no value assigned to it.
     */
    public MutableLiveData() {
        super();
    }
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }
    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

Transformations.map()

在数据分发给观察者之前对其中存储的值进行更改,返回一个新的 LiveData,可以使用此方法。

val strLiveData = MutableLiveData<String>()
 val strLengthLiveData = Transformations.map(strLiveData) {
    it.length
 }
 strLiveData.observe(this) {
   Log.e("---345---> str:", "$it");
 }
 strLengthLiveData.observe(this) {
  Log.e("---345---> strLength:", "$it");
 }
strLiveData.value = "hello word"
E/---345---> str:: hello word
E/---345---> strLength:: 10

Transformations.switchMap()

对上面的做了个判断,根据不同的需求返回不同的 LiveData。也可以通过 id 去判断,返回对应的 livedata 即可。

val idLiveData = MutableLiveData<Int>()
val userLiveData = Transformations.switchMap(idLiveData) { id->
      getUser(id)
}

合并多个 LiveData

val live1 = MutableLiveData<String>()
val live2 = MutableLiveData<String>()
val mediator = MediatorLiveData<String>()
mediator.addSource(live1) {
  mediator.value  = it
  Log.e("---345---> live1", "$it");
}
mediator.addSource(live2){
  mediator.value  = it
  Log.e("---345---> live2", "$it");
}
mediator.observe(this, Observer {
  Log.e("---345---> mediator", "$it");
})
live1.value = "hello"
E/---345---> mediator: hello
E/---345---> live1: hello

通过 MediatorLiveData 将两个 MutableLiveData 合并到一起,这样当任何一个发生变化,MediatorLiveData 都可以感知到。

五、LiveData分发问题

数据粘性事件

例如再没有观察者的时候发送数据,此时 mVersion +1,等到真正添加了观察者后,生命周期也是活跃的,那么就会将这个数据重新分发到观察者。所以说发送数据这个操作是粘性的。

如果需要去除粘性事件,可以在添加完 observe 后去通过反射修改 mVersion 和 观察者包装类中的 mLastVersion 的值,将 mVersion 赋值给 mLastVersion 即可去掉粘性事件。

数据倒灌现象

一般情况下,LiveData 都是存放在 ViewModel 中的,当Activity重建的时候,观察者会被 remove 掉,重建后会添加一个新的观察者,添加后新的观察者版本号就是 -1,所以就会出现数据再次被接收到。

这种解决方式和上面一样,反射修改版本号就可以解决。非活跃状态的观察者转为活跃状态后,只能接收到最后一次发送的数据。一般情况下我们都需要的是最新数据,如果非要所有数据,只能重写 LiveData 了。

到此这篇关于Android Jetpack组件库LiveData源码深入探究的文章就介绍到这了,更多相关Android Jetpack LiveData内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android Jetpack组件支持库DataBinding与ViewModel与LiveData及Room详解

    目录 一.官方推荐的Jetpack架构 二.添加依赖 三.创建Repository 四.创建ViewModel 五.activity中使用 Android Jetpack之ViewModel.LiveData Android Jetpack之LifeCycle 一.官方推荐的Jetpack架构 ViewModel是介于View(视图)和Model(数据模型)之间的中间层,能够使视图和数据分离,又能提供视图和数据之间的通信. LiveData是一个能够在ViewModel中数据发生变化时通知页面刷

  • Android开发中用Kotlin编写LiveData组件教程

    目录 1.简单使用 2.map和switchMap LiveData是Jetpack提供的一种响应式编程组件,它可以包含任何类型的数据,并在数据发生变化的时候通知给观察者.也就是说,我们可以将数据使用LiveData来包装,然后在Activity中去观察它,就可以主动将数据变化通知给Activity了. 1.简单使用 class MainViewModel(countReserved:Int) : ViewModel() { /*当外部调用counter变量时,实际上获得的就是_counter的

  • Android开发Jetpack组件ViewModel与LiveData使用讲解

    目录 一.ViewModel 1.解决的问题 2.注意点事项 3.ViewModel案例 二.LiveData 1.viewmodel+livedata使用案例 2.viewmodel+livedata Android Jetpack之LifeCycle 一.ViewModel ViewModel是介于View(视图)和Model(数据模型)之间的中间层,能够使视图和数据分离,又能提供视图和数据之间的通信.如图所示: 1.解决的问题 屏幕翻转后页面数据的丢失: 异步调用导致的内存泄露: 类膨胀提

  • Android 自定义Livedata使用示例解析

    目录 前言 Livedata分析 自定义Livedata 总结 前言 我们在开发中在使用MVVM的情况下经常会配合livedata来达到快速开发的效果,但是一般都是在activity或者fragment中去使用,我今天想介绍一种自定义的方式,如果你有复杂的自定义View或者某些场景,也可以使用livedata来达到一个很不错的效果. Livedata分析 我们平时使用livedata都会在activity或者fragment中使用,配合 Lifecycle就不用管理生命周期什么的了,所以一般以a

  • Android Jetpack 狠活Lifecycles与LiveData使用详解

    目录 前言 正篇 结语 前言 今天在工作时,测试突然提了一个Bug给我,要求我将APP中某活动页面的UI界面要根据用户在由此页面跳转的下个页面操作,在返回时要实时更新. 在检查代码时,发现我已经对界面可变数据用LiveData去观测,但由于页面变化后并没有重新初始化UI,所以我放在初始化UI的请求根本没有起效,如上图所示例子,在进入下一个页面如若关闭开关,返回时无法及时更新,于是我便想到了安卓科技与狠活Lifecycles,去监听onResume,在resume时用livedata去post数据

  • Android Jetpack组件库LiveData源码深入探究

    目录 前言 一.LiveData 二.使用案例 三.LiveData 实现原理 四.LiveData 相关源码 五.LiveData分发问题 Android Jetpack之ViewModel.LiveData Android Jetpack之LifeCycle Android Jetpack之DataBinding+ViewModel+LiveData+Room 前言 Jetpack是一个由多个技术库组成的套件,可帮助开发者遵循最佳做法,减少样板代码并编写可在各种Android版本和设备中一致

  • Android Jetpack 组件LiveData源码解析

    目录 前言 基本使用 疑问 源码分析 Observer ObserverWrapper LifecycleBoundObserver MutableLiveData postValue setValue 问题答疑 LiveData 特性引出的问题 问题解决 最后 前言 本文来分析下 LiveData 的源码,以及其在实际开发中的一些问题. 基本使用 一般来说 LiveData 都会配合 ViewModel 使用,篇幅原因关于 ViewModel 的内容将在后续博客中分析,目前可以将 ViewMo

  • Android JetPack组件的支持库Databinding详解

    目录 简介 启用databinding 布局xml variable (变量标签) data (数据标签) @{}表达式 绑定普通数据 绑定可观察数据 对单个变量的绑定-fields 对集合的绑定-collections 绑定对象-objects 绑定LiveData 双向绑定 简介 DataBinding 是 Google 在 Jetpack 中推出的一款数据绑定的支持库,利用该库可以实现在页面组件中直接绑定应用程序的数据源.使其维护起来更加方便,架构更明确简介. DataBinding 唯一

  • Mango Cache缓存管理库TinyLFU源码解析

    目录 介绍 整体架构 初始化流程 读流程 写流程 事件处理机制 主流程 write 清理工作 缓存管理 什么是LRU? 什么是SLRU? 什么是TinyLFU? mango Cache中的TinyLFU counter counter的初始化 counter的使用 lruCache slruCache filter TinyLFU的初始化 TinyLFU写入 TinyLFU访问 增加entry的访问次数 估计entry访问次数 总结 介绍 据官方所述,mango Cache是对Guava Cac

  • Android Jetpack组件中LifeCycle作用详细介绍

    目录 Jetpack 1.那么Jetpack是什么呢 2.为何使用Jetpack 3.Jetpack与AndroidX LifeCycle 1.LifeCycle的作用 2.LifeCycle应用 1.设计组件 2.使用组件 3.总结LifeCycle的使用 Jetpack Jetpack,我觉得翻译为“飞行器”更好听,因为Google针对编程历史乱象,整理出一套组件库,帮助开发者创造更完美的应用作品.现在市面上,很多公司招聘面试要求渐渐把Jetpack看作必会技能,Google也在疯狂的安利J

  • Android Jetpack组件ViewModel基本用法详解

    目录 引言 一.概述与作用 二.基本用法 小结 引言 天道好轮回,终于星期五,但是还是忙碌了一天.在项目中,我遇到了一个问题,起因则是无法实时去获取信息来更新UI界面,因为我需要知道我是否获取到了实时信息,我想到的办法有三,利用Handler收发消息在子线程与主线程切换从而更新信息,其二则是利用在页面重绘的时候(一般是页面变动如跳转下个页面和将应用切至后台),其三就是利用Jetpack中最重要的组件之一ViewModel,最后我还是选择了ViewModel,因为感觉更方便. 其实想到的前面两个方

  • flutter图片组件核心类源码解析

    目录 导语 问题 Image的核心类图及其关系 网络图片的加载过程 网络图片数据的回调和展示过程 补上图片内存缓存的源码分析 如何支持图片的磁盘缓存 总结 导语 在使用flutter 自带图片组件的过程中,大家有没有考虑过flutter是如何加载一张网络图片的? 以及对自带的图片组件我们可以做些什么优化? 问题 flutter 网络图片是怎么请求的? 图片请求成功后是这么展示的? gif的每一帧是怎么支持展示的? 如何支持图片的磁盘缓存? 接下来,让我们带着问题一起探究flutter 图片组件的

  • 自定义Android六边形进度条(附源码)

    本文实例讲述了Android自定义圆形进度条,分享给大家供大家参考.具体如下: 大家也可以参考这两篇文章进行学习: <自定义Android圆形进度条(附源码)>   <Android带进度的圆形进度条> 运行效果截图如下: 主要代码: package com.sxc.hexagonprogress; import java.util.Random; import android.content.Context; import android.content.res.ColorSta

  • Android 自定义相机及分析源码

    Android 自定义相机及分析源码 使用Android 系统相机的方法: 要想让应用有相机的action,咱们就必须在清单文件中做一些声明,好让系统知道,如下 <intent-filter> <action android:name="android.intent.action.IMAGE_CAPTURE" /> <category android:name="android.intent.category.DEFAULT" />

随机推荐