Jetpack之livedata原理分析

news2024/11/25 3:03:54

1.LiveData是什么?

在这里插入图片描述
只有在生命周期处于started和resumed时。livedata才会更新观察者
在这里插入图片描述

2.Livedata的各种使用方式

1.更新数据

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val textView : TextView = findViewById(R.id.tv_textview)

        // 1 观察者 眼睛 环节
        MyLiveData.info1.observe(this, {
            textView.text = it // 更新UI
        })

        // 完整写法 new  Observer  onChanged
        MyLiveData.info1.observe(this, object: Observer<String> {
            override fun onChanged(t: String?) {
                textView.text = t // 更新UI
            }
        })

        // 完整写法 new  Observer  onChanged
        MyLiveData.info1.observe(this, object: Observer<String> {
            override fun onChanged(t: String?) {
                textView.text = t // 更新UI
            }
        })


        // 完整写法 new  Observer  onChanged
        MyLiveData.info1.observe(this, object: Observer<String> {
            override fun onChanged(t: String?) {
                textView.text = t // 更新UI
            }
        })


        // 完整写法 new  Observer  onChanged
        MyLiveData.info1.observe(this, object: Observer<String> {
            override fun onChanged(t: String?) {
                textView.text = t // 更新UI
            }
        })




        // 2 触发数据改变 环节
        MyLiveData.info1.value = "default" // setValue 主线程

        thread {
            Thread.sleep(3000)
            MyLiveData.info1.postValue("三秒钟后,修改了哦")  // postValue 子线程
        }

        thread { // 子线程
            Thread.sleep(6000)
            MyLiveData.info1.postValue("六秒钟后,修改了哦")  // postValue 子线程
        }
    }

}
object MyLiveData { // 单例

    // 懒加载
    val info1: MutableLiveData<String> by lazy { MutableLiveData() }

}

2.模拟后台推送

class MainActivity2 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)

        // 启动服务
        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            startService(Intent(this, MyService::class.java))
            Toast.makeText(MainActivity2@this, "推送服务器启动成功", Toast.LENGTH_SHORT).show()
        }

        // 一股脑执行,他不会去检测生命周期
        // snedMessage(handler)


        // 眼睛 观察者  微信列表可见的情况下,才能做事情
        MyLiveData.data1.observe(this, {
            Log.d("server", "界面可见,说明用户在查看微信列表界面啦,更新消息列表UI界面:${it}")
            Toast.makeText(this, "更新消息列表UI界面成功:${it}", Toast.LENGTH_SHORT).show()
        })
    }
}
object MyLiveData { // 单例

    // 这里为info1的MutableLiveData 懒加载初始化(懒加载:用到时才加载)
    val data1 : MutableLiveData<String> by lazy { MutableLiveData() }

    init {
        // data1.value = "default" // 违背在 子线程 setValue
        data1.postValue("DDD") // 子线程 执行 postValue 非常OK
    }
}
// 模拟后台推送
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..100000) {
                Log.d("server", "服务器给推你推送消息啦(叮咚声响),消息内容是:${x}")
                MyLiveData.data1.postValue("服务器给推你推送消息啦,消息内容是:${x}")
                Thread.sleep(2000) // 5秒钟推一次
            }
        }
        return super.onStartCommand(intent, flags, startId)
    }
}

3.数据粘性

class MainActivity3 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main3)


        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            // version++  == 0   第一步
            MyLiveData.value1.value = "我就是我,不一样的烟火1" // 以前的旧数据
            MyLiveData.value1.value = "我就是我,不一样的烟火2" // 以前的旧数据
            MyLiveData.value1.value = "我就是我,不一样的烟火3" // 以前的旧数据
            MyLiveData.value1.value = "我就是我,不一样的烟火4" // 以前的旧数据
            MyLiveData.value1.value = "我就是我,不一样的烟火5" // 以前的旧数据

            startActivity(Intent(this, MainActivity4::class.java))
        }
    }
}
// this@MainActivity4 每个类都有符号
class MainActivity4 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main4)

        // 我后观察数据,居然能够收到 前面修改的数据,这就是 数据黏性
        /*MyLiveData.value1.observe(this, {
            Toast.makeText(this, "观察者数据变化:$it", Toast.LENGTH_SHORT).show()
        })*/

        // 第二步
        MyLiveData.value1.observe(this, object: Observer<String>{
            override fun onChanged(t: String?) {
                Toast.makeText(this@MainActivity4, "观察者数据变化$t", Toast.LENGTH_SHORT).show()
            }
        })

        /*// 此观察者 和 handler没有区别,一股脑的执行 (极端的情况,可以用)
        // 手动考虑释放工作
        MyLiveData.value1.observeForever({

        })

        //  // 关心的新数据
        MyLiveData.value1.postValue("1111")

        //  // 关心的新数据
        MyLiveData.value1.postValue("1111")


        //  // 关心的新数据
        MyLiveData.value1.postValue("1111")

        //  // 关心的新数据
        MyLiveData.value1.postValue("1111")


        // 先订阅
        ok(object : Callback {
            override fun show() {
            }
        })*/
    }

    fun ok(callback: Callback) {
        // 后触发
        callback.show()
    }

    override fun onStart() {
        super.onStart()
    }


    override fun onDestroy() {
        super.onDestroy()
        // 手动释放
        // MyLiveData.value1.removeObserver()
    }
}
object MyLiveData {

    // 这里为info1的MutableLiveData 懒加载初始化(懒加载:用到时才加载)
    val value1 : MutableLiveData<String> by lazy { MutableLiveData() }

}

3.Livedata源码解析

在这里插入图片描述
下面来看observe方法做了哪些操作,observe方法的主要代码如下:

    /**
     * Adds the given observer to the observers list within the lifespan of the given
     * owner. The events are dispatched on the main thread. If LiveData already has data
     * set, it will be delivered to the observer.
     * <p>
     * The observer will only receive events if the owner is in {@link Lifecycle.State#STARTED}
     * or {@link Lifecycle.State#RESUMED} state (active).
     * <p>
     * If the owner moves to the {@link Lifecycle.State#DESTROYED} state, the observer will
     * automatically be removed.
     * <p>
     * When data changes while the {@code owner} is not active, it will not receive any updates.
     * If it becomes active again, it will receive the last available data automatically.
     * <p>
     * LiveData keeps a strong reference to the observer and the owner as long as the
     * given LifecycleOwner is not destroyed. When it is destroyed, LiveData removes references to
     * the observer &amp; the owner.
     * <p>
     * If the given owner is already in {@link Lifecycle.State#DESTROYED} state, LiveData
     * ignores the call.
     * <p>
     * If the given owner, observer tuple is already in the list, the call is ignored.
     * If the observer is already in the list with another owner, LiveData throws an
     * {@link IllegalArgumentException}.
     *
     * @param owner    The LifecycleOwner which controls the observer
     * @param observer The observer that will receive the events
     */
    @MainThread
    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方法首先会通过assertMainThread方法检查程序当前是否运行在主线程中,如果不是,则抛出一个异常。使用时需要确保observe方法是执行在主线程中的,且LiveData和生命周期相关联,如果当前状态是非活跃状态则不会执行。这里提一下,如果想让数据监测变化不受活动状态的影响,可以使用observeForever方法,这样Activity即使不处于活动状态,也可以接收到改变的数据,但当Activity销毁时,一定要主动调用removeObserver方法,否则LiveData会一直存在,这会导致内存泄漏。
activity中的addObserver传进来的只是一个接口,在这个接口上进行进一步封装为
LifecycleBoundObserver,保存了宿主的lifecycleowner;shouldBeActive() 是为了判断宿主是否为活跃状态。

/**
 * A simple callback that can receive from {@link LiveData}.
 *
 * @param <T> The type of the parameter
 *
 * @see LiveData LiveData - for a usage description.
 */
public interface Observer<T> {
    /**
     * Called when the data is changed.
     * @param t  The new data
     */
    void onChanged(T t);
}
    class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
        @NonNull
        final LifecycleOwner mOwner;

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

        @Override
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            activeStateChanged(shouldBeActive());
        }

        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }

LifecycleBoundObserver实现了LifecycleEventObserver接口,当页面发生改变的时候,程序会走到onStateChanged方法中。从上述源码可以看出,当页面被销毁时会调用removeObserver移除观察,所以使用LiveData的observe方法不用担心存在内存泄漏的风险。如果之前的周期与当前不同,则会同步一次状态,并调用activeStateChanged方法,而activeStateChanged方法则会调用dispatchingValue方法分发数据。dispatchingValue方法的代码如下:

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    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;
    }

这里通过使用mDispatchingValue变量标记来防止分发相同的内容,通过循环方式遍历所有的观察者,通过considerNotify方法更新数据。为的是时时刻刻读取lifecycle生命周期,获取到最新的生命周期状态:

    private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
        //
        // we still first check observer.active to keep it as the entrance for events. So even if
        // the observer moved to an active state, if we've not received that event, we better not
        // notify for a more predictable notification order.
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        //这里就是粘性的原因,如果不想粘性改成等于号即可
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData);
    }

如果观察者已经不属于活动状态,则直接返回,并通过比较数据的版本号判断数据是否需要更新。如果需要更新则会回调到observer的onChanged方法中,从而实现在UI层接收数据的回调。那么这个版本号什么时候会更新呢?来看设置数据的方法,这里以setValue方法为例,代码如下:

@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

可以看到,当调用setValue方法时,数据版本号会改变,并且会通过dispatching-Value方法进行数据处理,这样就实现了LiveData可观察的特性。

对于postvalue而言:

public class MutableLiveData<T> extends LiveData<T> {
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }

    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

利用handler将线程切换到主线程

    protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if (!postTask) {
            return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }
    @Override
    public void postToMainThread(Runnable runnable) {
        mDelegate.postToMainThread(runnable);
    }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

接下来我们看runnable的代码,本质上也是调用Setvalue:

    private final Runnable mPostValueRunnable = new Runnable() {
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            //noinspection unchecked
            setValue((T) newValue);
        }
    };
    @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/508106.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python数据分析实战【十四】:python的三种排序方法:sort、sorted、sort_values案例学习【文末源码地址】

文章目录 一、List.sort()排序案例一&#xff1a;按照列表中的元素进行排序案例二&#xff1a;按照销售额数据进行排列 二、sorted()排序案例一&#xff1a;sorted()对列表进行排序案例二&#xff1a;sorted()对字典进行排序案例三&#xff1a;sorted()对列表中的字典元素排序 …

【AI大模型】国产AI技术再创新高,讯飞星火认知大模型中文能力已经超越ChatGPT?

文章目录 前言SparkDesk讯飞星火认知大模型简介语言理解知识问答逻辑推理数学题解答代码理解与编写亲自体验写在最后 前言 5月6日&#xff0c;讯飞星火认知大模型成果发布会在安徽合肥举行。科大讯飞董事长刘庆峰、研究院院长刘聪发布讯飞星火认知大模型&#xff0c;现场实测大…

(一)ArcGIS空间数据的转换与处理——投影变换

ArcGIS空间数据的转换与处理——投影变换 原始数据往往由于在数据结构、数据组织、数据表达等方面与用户需求不一致而要进行转换与处理。本节主要介绍 ArGIS 中数据的投影变换内容。 目录 ArcGIS空间数据的转换与处理——投影变换 1.概述2.定义投影3.投影变换3.1栅格数据的投…

Python数据分析实战【十四】:Python的三种排序方法:sort()、sorted()和sort_values()【文末源码地址】

文章目录 一、List.sort()排序案例一&#xff1a;按照列表中的元素进行排序案例二&#xff1a;按照销售额数据进行排列 二、sorted()排序案例一&#xff1a;sorted()对列表进行排序案例二&#xff1a;sorted()对字典进行排序案例三&#xff1a;sorted()对列表中的字典元素排序 …

计算机网络 | 基于TCP的C/S模型代码实现

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

QT QGraphicsView 提升到 QChartView报错 解决方案

QT QGraphicsView 提升到 QChartView报错 解决方案 本文主要描述, 使用QT提供的QChartView来绘制图表,提升QGraphicsView控件继承QChartView后,然后将QGraphicsView提升到我们自己写的类,怎么才能确保提升后编译不报错. [问题描述] 使用QGraphicsView显示图表的时候,我们需要将…

基于Leaflet的乡镇行政区划在WebGIS中的可视化工具实践

前言 在构建WebGIS的应用系统中&#xff0c;通常会遇到以下的建设需求。功能点如下&#xff1a; 实现影像地图的展示&#xff0c;可以放大、缩小和浏览地图。地图的拖拽范围需要控制在合理的经纬度范围内。在影像地图侧边实现某乡镇级行政区的信息展示&#xff0c;包括名称&…

Java中的深拷贝和浅拷贝

目录 &#x1f34e;引出拷贝 &#x1f34e;浅拷贝 &#x1f34e;深拷贝 &#x1f34e;总结 引出拷贝 现在有一个学生类和书包类&#xff0c;在学生类中有引用类型的书包变量&#xff1a; class SchoolBag {private String brand; //书包的品牌private int size; //书…

使用Vue+vue-router+路由守卫实现路由鉴权功能实战

目录 一、本节介绍和上节回顾 1. 上节介绍 2. Vue + SpringBoot前后端分离项目实战的目录

探秘C语言:字符分类与转换函数,让你的程序更加精准和优美

本篇博客会讲解C语言ctype.h这个头文件中的2类好用的库函数&#xff0c;分别是字符分类函数和字符转换函数。 字符分类函数 字符分类函数&#xff0c;指的是判断一个字符是不是属于某个类别&#xff0c;如果属于这个类别&#xff0c;返回非0数&#xff1b;如果不属于这个类别…

MGA元宇宙创世大会 中国2022

MGA元宇宙创世大会 中国2022 主办方:MGA元宇宙创世联盟 协办方&#xff1a;增强现实核心技术产业联盟 元宇宙创世大会中国2022将包含两场主题峰会&#xff0c;一个是虚拟现实与增强现实峰会&#xff0c;一个是NFT与区块链峰会。涵盖元宇宙最重要的两大支撑技术&#xff08;VR/…

BrightID与Poap使用注册说明

把这两个app一起介绍&#xff0c;主要是因为这两个app是获取gitcoin及其他一些平台空投的前提条件&#xff0c;而且这两个app本身也会有一些诸如token、NFT之类的奖励。 BrightID BrightID是一个web3的身份证&#xff0c;用来证明当前操作的行为是你本人。由于验证流程的唯一…

chanmama响应数据解析

0x00目标url aHR0cHM6Ly93d3cuY2hhbm1hbWEuY29tL2F1dGhvckRldGFpbC85OTI0MjExODcxOC9wcm9tb3Rpb24 0x01接口分析 简单的get 但是返回数据被加密了 这里我们就来想想怎么解密这些数据。首先后端发来的数据是加密的&#xff0c;但是我们在前端看到的可不是加密后的数据。前端…

Rust + WASM 入门

一、参考资料 参考官方技术文档 https://rustwasm.github.io/ 二、安装脚手架 cargo-generate # cargo-generate 用于快速生成 WASM 项目的脚手架&#xff08;类似 create-react-app&#xff09; cargo install cargo-generate 三、下载安装 wasm-pack.exe 打包工具 双击安装…

大数据湖体系规划与建设方案(ppt可编辑)

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除。 业界主流公司对于数据湖的规划 — IBM IBM 公司提出的数据湖架构&#xff0c;包括六大关键部件&#xff1a;数据湖资源库按照数据特点进行原始格式的分类存储库企业IT交互统…

【新星计划-2023】详解交换机的工作原理、功能与作用

交换机有多个端口&#xff0c;每个端口都具有桥接功能&#xff0c;可以连接一个局域网或一台高性能服务器或工作站&#xff0c;实际上&#xff0c;交换机有时被称为多端口网桥。那么&#xff0c;对于交换机的工作原理这块你是否有了解呢&#xff1f;接下来我们就来为大家详细介…

Android程序员如何面临被优化(亲身经历与看法)

前言 相信大家都有过这样一个经历&#xff0c;就是在一家公司工作久了&#xff0c;能轻松的完成每天的工作内容&#xff0c;无论是在大公司还是其他小公司&#xff0c;这样的状态时间长了之后&#xff0c;公司领导就会认为你每天不做事&#xff0c;总暗中招一些工资低的人代替…

java基础知识梳理

虽然已经在实际工作中与java打交道5年之多&#xff0c;但是一直没系统地对java这门语言进行梳理和总结&#xff0c;掌握的知识也比较零散。恰好利用这段时间重新认识下java&#xff0c;并对一些常见的语法和知识点做个总结与回顾&#xff0c;一方面为了加深印象&#xff0c;方便…

OtterCTF---Memory Forensics内存取证(1-13)

一.OtterCTF 内存取证 CTF地址&#xff1a; OtterCTF 国产化一下&#xff1a; 注册一下 登录就可以 &#xff08;注&#xff1a;因为邮箱不验证&#xff0c;随意搞个就可以&#xff09;&#xff1a; 1 - What the password? 第一题&#xff1a; 国产化&#xff1a; 下载…

DEJA_VU3D - Cesium功能集 之 104-攻击箭头(标绘+编辑)

前言 编写这个专栏主要目的是对工作之中基于Cesium实现过的功能进行整合,有自己琢磨实现的,也有参考其他大神后整理实现的,初步算了算现在有差不多实现小140个左右的功能,后续也会不断的追加,所以暂时打算一周2-3更的样子来更新本专栏(每篇博文都会奉上完整demo的源代码,…