Jetpack之livedata原理

news2024/10/7 12:25:21

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/507589.html

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

相关文章

c++之函数对象和谓词

目录 函数对象&#xff1a; 谓词&#xff1a; 一元谓词函数举例如下 二元谓词举例如下 函数对象和函数的区别 一元谓词的案例 二元函数对象案例 二元谓词案例 函数对象&#xff1a; 重载函数调用操作符的类&#xff0c;其对象常称为函数对象&#xff08;function obj…

内网渗透之linux到linux横向移动-ssh

0x01 一般情况下SSH密钥存放在~/.ssh/目录下&#xff0c;也可以文件中搜索已保存的SSH凭证 ~/.ssh/config ~/.ssh/known_hosts ~/.bash_history grep -ir "BEGIN RSA PRIVATE KEY" /* grep -ir "BEGIN DSA PRIVATE KEY" /* grep -ir "BEGIN OPENSSH…

SpringBoot入门学习笔记-快速认识

SpringBoot入门学习笔记-快速认识 快速案例入门案例解析parentstarter引导类内嵌tomcat ) 快速案例 在controller定义一个类 package com.ustc.sp5.controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.…

redis 数据类型简介

redis 数据类型 redis的五种数据类型是&#xff1a;1、string&#xff08;字符串&#xff09;&#xff1b;2、hash&#xff08;哈希&#xff09;&#xff1b;3、list&#xff08;列表&#xff09;&#xff1b;4、set&#xff08;集合&#xff09;&#xff1b;5、sort set &…

QT初体验:手把手带你写一个自己的串口助手

前言 本文记录一下用QT Creator 写一个基本功能齐全的串口助手的过程&#xff0c;整个工程只有几百行代码&#xff0c;跟着做下来对新手来说可以更快了解整个QT项目的开发过程和一些常用控件的使用方法。对新手学习QT能增强信心&#xff0c;话不多说&#xff0c;正文开始 先看…

Mysql日志redo log、bin log、undo log 区别与作用及二阶段提交

一、redo log 重做日志 作用&#xff1a;确保事务的持久性。防止在发生故障的时间点&#xff0c;尚有脏页未写入磁盘&#xff0c;在重启mysql服务的时候&#xff0c;根据redo log进行重做&#xff0c;从而达到事务的持久性这一特性。 内容&#xff1a;物理格式的日志&#x…

46-Dockerfile-USER/WORKDIR指令

USER/WORKDIR指令 前言USER作用格式使用示例 WORKDIR作用格式说明使用示例 前言 本篇来学习下Dockerfile中的USER/WORKDIR指令 USER 作用 指定运行容器时的用户名或 UID&#xff0c;后续的RUN等指令也会使用指定的用户身份 说明&#xff1a; USER 只是帮助我们切换到指定的…

12_Uboot启动流程_4

目录 images全局变量 do_bootz函数 bootz_start函数 do_bootm_states函数 bootm_os_get_boot_func函数 do_bootm_linux函数 images全局变量 不管是bootz还是bootm命令,在启动Linux内核的时候都会用到一个重要的全局变量:images, images在文件cmd/bootm.c中有如下定义: i…

【2023/05/09】Scratch

Hello&#xff01;大家好&#xff0c;我是霜淮子&#xff0c;2023倒计时第4天。 Share The mighty desert is burning for the love of a blade of grass who shaks her head and laughs and flies away. 译文&#xff1a; 无垠的沙漠热烈追求一叶绿草的爱&#xff0c;她摇摇…

PCL中点云分割算法简析

文章目录 前言一、点云分割算法简介1.1 基于RANSAC的点云分割1.2 基于聚类的点云分割1.2.1 欧式聚类分割 1.3 基于深度学习的点云分割 二、算法示例2.1 基于RANSAC的平面分割2.2 欧式聚类2.3 基于PointNet的点云分割 总结 前言 点云分割算法广泛应用于激光遥感、无人驾驶、工业…

centos安装nginx教程

安装所需环境 Nginx 是 C语言 开发&#xff0c;建议在 Linux 上运行&#xff0c;当然&#xff0c;也可以安装 Windows 版本&#xff0c;本篇则使用 CentOS 7 作为安装环境。 一. gcc 安装 安装 nginx 需要先将官网下载的源码进行编译&#xff0c;编译依赖 gcc 环境&#xff0c…

【ESD专题】案例:TVS管钳位电压能不能通过TLP测试数据表征?

这几天遇到一个问题,就是还是想说TVS管导入的时候需要进行IEC61000-4-2 8kV接触静电的钳位波形测试。 比如有时可以看到规格书中给出对应的在IEC61000-4-2 8kV接触时的真实钳位波形: 根据我们文章【ESD专题】TVS管的选择的误区及钳位电压测试方法和一些参考手册所说…

计算机操作系统第四版第七章文件管理—课后习题答案

1.何谓数据项、记录和文件&#xff1f; 数据项&#xff1a;是最低级的数据组织形式&#xff0c;可以分为两种类型&#xff1a;基本数据项和组合数据项。基本数据项是用于描述一个对象的某种属性的字符集&#xff0c;是数据组织中可以命名的最小逻辑数据单位&#xff0c;又称为字…

使用volta对node版本进行控制

安装volta 首先下载volta 下载完成之后在电脑上使用命令行工具查看是否安装成功 volta -v 使用 volta -h 命令可以查看volta的一些用法 安装全局的node版本,可以有三种,第一种是安装最新的,第二种是安装某一个大版本下的,第三种是安装指定的node版本(安装的时候需要等待一段时…

Rest风格复习

Rest风格复习 简介三种注解的风格快速开发 简介 通过一段路径和访问方式来确定访问资源的行为方式 使用POST方式 // value定义 路径 method定义访问的方式RequestMapping(value "/users",method RequestMethod.POST)ResponseBodypublic String save(){System.out.…

Andrew Ng和OpenAI教你写prompt

课程地址&#xff1a; https://learn.deeplearning.ai/chatgpt-prompt-engb站搬运&#xff1a; https://www.bilibili.com/video/BV1No4y1t7Zn 教学人员&#xff1a;Lsa Fulford&#xff0c; Andrew NG LLM的两种样式 Base LLM:基于文本训练数据预测下一个词的概率&#xff0…

制作Alpine Linux镜像报错errors: 15 distinct packages available

1.执行报错 执行docker build -t 镜像:版本 -f Dockerfile . 报错&#xff1a; 2.查看网上的解决思路 网上文档解决思路&#xff1a; 这边我做了一下改变把这些写入了dockerfile 加了几个RUN RUN rm -rf /var/cache/apk RUN mkdir -p /var/cache/apk RUN apk update -v 发现还…

Java基本数据类型详解及应用示例

Java作为一门强类型语言&#xff0c;基本数据类型是非常重要的概念。Java中基本数据类型分为四种类别&#xff1a;整数类型、浮点数类型、字符类型和布尔类型。其中&#xff0c;每一种数据类型都有着不同的占用字节数和表示范围&#xff0c;合理使用选择不同的数据类型可以提高…

数组低效的“插入”和“删除”

插入操作 假设数组的长度为 n&#xff0c;现在&#xff0c;如果我们需要将一个数据插入到数组中的第 k 个位置。为了把第 k 个位置腾出来&#xff0c;给新来的数据&#xff0c;我们需要将第 k&#xff5e;n 这部分的元素都顺序地往后挪一位。那插入操作的时间复杂度是多少呢&a…

ChatGPT 中文指令指南,教会你如何使用chatgpt实现中文你想要的答案

&#x1f9e0; ChatGPT 中文指令指南&#xff0c;教会你如何使用chatgpt实现中文你想要的答案 1.学习英语–替代词典 App 场景例子Prompts解释中文英文意思&#xff0c;并解释单词的词根词缀。可以替代词典。告诉我 Egocentric 的词性和音标&#xff0c;并使用中文和英文解释…