JetPack之ViewModel+LiveData

目录

  • 一、概述
  • 二、LiveData 使用
    • 2.1 创建 LiveData 对象
    • 2.2 观察 LiveData 对象
    • 2.3 更新 LiveData 对象
  • 三、编写 LiveData Demo
    • 3.1 不使用 LiveData
    • 3.2 使用 MutableLiveData
    • 3.3 使用 MediatorLiveData
      • 3.3.1 监听 2 个数据源的变化
      • 3.3.2 编写模拟 2 个数据源更新的代码
  • 四、ViewModel 使用
    • 4.1 创建 ViewModel 类
    • 4.2 在 Activity 中使用
  • 五、ViewModel+LiveData
    • 5.1 在 ViewModel 中增加 LiveData
    • 5.2 在 ViewModel 中添加操作数据的逻辑
    • 5.3 修改 MainActivity 中和 liveData 有关的代码
    • 5.4 修改 MyViewModel
    • 5.5 修改后
      • 5.5.1 修改后MainActivity
      • 5.5.2 修改后MyViewModel
  • 六、总结


一、概述

之前单独介绍过ViewModel和LiveData,但是没有将两者结合使用。
JetPack之ViewModel
JetPack之LiveData
ViewModel 对象为特定的界面组件(如 Fragment 或 Activity)提供数据,并包含数据处理业务逻辑,会配合 LiveData 一起使用。
LiveData 是一种可观察的数据存储器类, LiveData 使用观察者模式,每当数据发生变化时, LiveData 会通知 Observer 对象,可以在这些 Observer 对象中更新 UI。
接下来,先介绍如果使用 LiveData,并编写一个 LiveData Demo,接着再结合ViewModel 对 LiveData Demo 的代码进行重构


二、LiveData 使用

LiveData< T>是一个抽象类,它有 2 个子类分别是: MutableLiveData和MediatorLiveData< T>,在编写代码时,是创建的子类。先来看MutableLiveData< T>使用方法,在后面的示例中再介绍如何使用 MediatorLiveData< T>

2.1 创建 LiveData 对象

如果要观察的对象类为 String,就通过如下代码创建一个 MutableLiveData 对象

MutableLiveData< String> liveData = new MutableLiveData<>();

2.2 观察 LiveData 对象

通过 observe 方法来监听 LiveData 的数据变化,每当 LiveData 发生变化时,都会回调
onChanged 方法,并返回值的内容 String s

liveData.observe(this, new Observer< String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "onChanged: " + s)}
});

2.3 更新 LiveData 对象

通过 LiveData 的 setValue 方法,给 LiveData 赋值,每次赋完值后,都会回调 onChanged 方法

liveData.setValue("add for test");

当给 LiveData 赋值为"add for test"时, onChanged 会输出日志:

I/LiveDataDemo: onChanged: add for test

三、编写 LiveData Demo

创建一个 Demo,主界面只有一个 TextView, TextView 用作展示一个数字,这个数字会从 59 开始显示,然后每隔 1 秒数字会减 1,直到数字变为 0。

3.1 不使用 LiveData

代码说明:
1、在 MainActivity 增加一个 countDown 的方法,通过 CountDownTimer 创建一个倒计时器
2、 new CountDownTimer(1 * 60 * 1000, 1 * 1000)中 2 个参数的含义:第一个参数表示这个 Timer 的总时长为 60 秒;第 2 个参数表示每隔 1 秒中会更新一次,并回调 onTick方法
3、每次调用 onTick 方法,会调用 textView.setText(String.valueOf(l / 1000));设置一次TextView 的内容
对应代码如下:

public class MainActivity extends AppCompatActivity {private TextView textView;@SuppressLint("MissingInflatedId")@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView = findViewById(R.id.textView);countDown();}private void countDown() {new CountDownTimer(1 * 60 * 1000, 1 * 1000) {@Overridepublic void onTick(long l) {textView.setText(String.valueOf(l / 1000));}@Overridepublic void onFinish() {}}.start();}
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

效果:
在这里插入图片描述
弊端:
业务逻辑(倒计时的功能)和 UI 逻辑(TextView 更新)没有分开,耦合到一起了

3.2 使用 MutableLiveData

为了方便演示,直接在 MainActivity 中操作 LiveData,实际项目中不要这样编写代码,应该把LiveData 放到 ViewModel 中
Google 推荐的使用 LiveData 的方法:
在这里插入图片描述

代码说明:
1、在 MainActivity 中增加一个 long 类型的 MutableLiveData
2、每次回调 onTick 方法时,调用 liveData.setValue(l);,把最新的值设置给 LiveData
3、调用 LivewData 的 observe 方法,设置一个监听器,每当 LiveData 的值变化时,会回调 onChanged 方法,在 onChanged 中设置 TextView 的内容
对应代码如下:

public class MainActivity extends AppCompatActivity {private final MutableLiveData<Long> liveData = new MutableLiveData<>();private TextView textView;@SuppressLint("MissingInflatedId")@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView = findViewById(R.id.textView);countDown();// TODO:为了方便演示,直接在 MainActivity 中操作 LiveData,实际项目中不要这样编写代码,应该把 LiveData 放到 ViewModel 中liveData.observe(this, new Observer<Long>() {@Overridepublic void onChanged(Long aLong) {textView.setText(String.valueOf(aLong / 1000));}});countDown();}public void countDown() {new CountDownTimer(1 * 60 * 1000, 1 * 1000) {@Overridepublic void onTick(long l) {// TODO:为了方便演示,直接在 MainActivity 中操作 LiveData,实际项目中不要这样编写代码,应该把 LiveData 放到 ViewModel 中liveData.setValue(l);}@Overridepublic void onFinish() {}}.start();}}

效果与上面一样。

3.3 使用 MediatorLiveData

MediatorLiveData 可以合并多个 LiveData 源,只要任何原始的 LiveData 源对象发生更改,就会触发 MediatorLiveData 对象的观察者。
例如,如果界面中有可以从本地数据库或网络更新的 LiveData 对象,则可以向MediatorLiveData 对象添加以下源:

与存储在数据库中的数据关联的LiveData 对象。
与从网络访问的数据关联的LiveData 对象。

Activity 只需观察 MediatorLiveData 对象即可从这两个源接收更新。接下来,实现这个例子。

3.3.1 监听 2 个数据源的变化

代码说明如下:
1、创建 1 个 MediatorLiveData< String>对象
2、创建 2 个MutableLiveData: liveData1 表示网络数据源, liveData2 表示本地数据源
3、调用 MediatorLiveData 的 addSource 方法,分别将 liveData1 和 liveData2 加到 MediatorLiveData 要监听的数据源中
4、设置 MediatorLiveData 的监听,当 2 个数据源发生变化时,会回调onChanged 方法,并将变化的内容展示到 TextView 上

3.3.2 编写模拟 2 个数据源更新的代码

编写一个 mergeTes 的方法,里面创建了 2 个 Timer,用作模拟网络数据更新和本地数据更新的情况,每隔 3 秒会往网络数据 liveData1 设置值、每隔 10 秒会往本地数据liveData2 设置值。
完整代码如下

public class MainActivity extends AppCompatActivity {private TextView textView;private MutableLiveData< String> liveData1 = new MutableLiveData<>();private MutableLiveData< String> liveData2 = new MutableLiveData<>();final MediatorLiveData< String> liveDataMerger = new MediatorLiveData<>();@SuppressLint("MissingInflatedId")@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView = findViewById(R.id.textView);liveDataMerger.addSource(liveData1, new Observer< String>() {@Overridepublic void onChanged(String s) {liveDataMerger.setValue(s);}});liveDataMerger.addSource(liveData2, new Observer< String>() {@Overridepublic void onChanged(String s) {liveDataMerger.setValue(s);}});liveDataMerger.observe(this, new Observer< String>() {@Overridepublic void onChanged(String s) {textView.setText(s);}});mergeTest();}public void mergeTest() {new CountDownTimer(1 * 60 * 1000, 3 * 1000) {@Overridepublic void onTick(long l) {liveData1.setValue("网络有数据更新了" + l/1000);}@Overridepublic void onFinish() {}}.start();new CountDownTimer(1 * 60 * 1000, 10 * 1000) {@Overridepublic void onTick(long l) {liveData2.setValue("本地数据库更新了" + l/1000);}@Overridepublic void onFinish() {}}.start();}
}

运行效果

在这里插入图片描述

四、ViewModel 使用

4.1 创建 ViewModel 类

创建一个 MyViewModel 类,继承自 ViewModel

import androidx.lifecycle.ViewModel;
public class MyViewMode extends ViewModel {}

4.2 在 Activity 中使用

    public class MainActivity extends AppCompatActivity {private MyViewModel viewModel;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);viewModel = new ViewModelProvider(this).get(MyViewModel.class);}}

五、ViewModel+LiveData

5.1 在 ViewModel 中增加 LiveData

在 MyViewModel 中增加 3 个 MutableLiveData,分别对应在 MainActivity 中使用到的。
并编写这 3 个 LiveData 的 get 方法

public class MyViewModel extends ViewModel {private MutableLiveData<Long> liveData = new MutableLiveData<>();private MutableLiveData<String> liveData1 = new MutableLiveData<>();private MutableLiveData<String> liveData2 = new MutableLiveData<>();public MutableLiveData<Long> getLiveData() {return liveData;}public MutableLiveData<String> getLiveData1() {return liveData1;}public MutableLiveData<String> getLiveData2() {return liveData2;}}

5.2 在 ViewModel 中添加操作数据的逻辑

countDown、 mergeTest 这 2 个方法在操作数据,从 MainActivity 中把这 2 个方法copy 到 MyViewModel 中

    public void countDown() {new CountDownTimer(1 * 60 * 1000, 1 * 1000) {@Overridepublic void onTick(long l) {liveData.postValue(l);}@Overridepublic void onFinish() {}}.start();}public void mergeTest() {new CountDownTimer(1 * 60 * 1000, 3 * 1000) {@Overridepublic void onTick(long l) {liveData1.postValue("网络有数据更新了" + l / 1000);}@Overridepublic void onFinish() {}}.start();new CountDownTimer(1 * 60 * 1000, 10 * 1000) {@Overridepublic void onTick(long l) {liveData2.postValue("本地数据库更新了" + l / 1000);}@Overridepublic void onFinish() {}}.start();}

5.3 修改 MainActivity 中和 liveData 有关的代码

1、删除 countDown,改用 viewModel.countDown();

    MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);viewModel.countDown();

2、用 viewModel.getLiveData().observe 替换 liveData.observe

    viewModel.getLiveData().observe(this, new Observer<Long>() {@Overridepublic void onChanged(Long aLong) {textView.setText(String.valueOf(aLong / 1000));}});

5.4 修改 MyViewModel

增加 liveDataMerger 字段,并编写 getLiveDataMerger()方法,这个方法里面的内容,是从 MainActivity 中 copy 过来的

public class MyViewModel extends ViewModel {private MediatorLiveData<String> liveDataMerger;private MutableLiveData<Long> liveData = new MutableLiveData<>();private MutableLiveData<String> liveData1 = new MutableLiveData<>();private MutableLiveData<String> liveData2 = new MutableLiveData<>();public MediatorLiveData<String> getLiveDataMerger() {if (liveDataMerger == null) {liveDataMerger = new MediatorLiveData<>();}liveDataMerger.addSource(liveData1, new Observer<String>() {@Overridepublic void onChanged(String s) {liveDataMerger.setValue(s);}});liveDataMerger.addSource(liveData2, new Observer<String>() {@Overridepublic void onChanged(String s) {liveDataMerger.setValue(s);}});return liveDataMerger;}
.........

5.5 修改后

5.5.1 修改后MainActivity

public class MainActivity extends AppCompatActivity {private TextView textView;private MyViewModel viewModel;@SuppressLint("MissingInflatedId")@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView = findViewById(R.id.textView);viewModel = new ViewModelProvider(this).get(MyViewModel.class);viewModel.getLiveDataMerger().observe(this, new Observer<String>() {@Overridepublic void onChanged(String s) {textView.setText(s);}});viewModel.mergeTest();
//        viewModel.getLiveData().observe(this, new Observer<Long>() {
//            @Override
//            public void onChanged(Long aLong) {
//                textView.setText(String.valueOf(aLong / 1000));
//            }
//        });
//        viewModel.countDown();}
}

5.5.2 修改后MyViewModel

public class MyViewModel extends ViewModel {private MutableLiveData<Long> liveData = new MutableLiveData<>();private MutableLiveData<String> liveData1 = new MutableLiveData<>();private MutableLiveData<String> liveData2 = new MutableLiveData<>();private MediatorLiveData<String> liveDataMerger;public MediatorLiveData<String> getLiveDataMerger() {if (liveDataMerger == null) {liveDataMerger = new MediatorLiveData<>();}liveDataMerger.addSource(liveData1, new Observer<String>() {@Overridepublic void onChanged(String s) {liveDataMerger.setValue(s);}});liveDataMerger.addSource(liveData2, new Observer<String>() {@Overridepublic void onChanged(String s) {liveDataMerger.setValue(s);}});return liveDataMerger;}public MutableLiveData<Long> getLiveData() {return liveData;}public MutableLiveData<String> getLiveData1() {return liveData1;}public MutableLiveData<String> getLiveData2() {return liveData2;}public void countDown() {new CountDownTimer(1 * 60 * 1000, 1 * 1000) {@Overridepublic void onTick(long l) {liveData.postValue(l);}@Overridepublic void onFinish() {}}.start();}public void mergeTest() {new CountDownTimer(1 * 60 * 1000, 3 * 1000) {@Overridepublic void onTick(long l) {liveData1.postValue("网络有数据更新了" + l / 1000);}@Overridepublic void onFinish() {}}.start();new CountDownTimer(1 * 60 * 1000, 10 * 1000) {@Overridepublic void onTick(long l) {liveData2.postValue("本地数据库更新了" + l / 1000);}@Overridepublic void onFinish() {}}.start();}
}

六、总结

ViewModel 和 LiveData 结合使用的一些好处:

  • 生命周期感知:ViewModel 和 LiveData 都是生命周期感知的组件,它们可以在界面控制器(如 Activity 或 Fragment)的生命周期变化时自动调整其行为。这意味着可以在 ViewModel 中存储和管理数据,而 LiveData 可以通知观察者(如界面控制器)数据的变化。
  • 数据持久性:ViewModel 和 LiveData 结合使用可以确保数据在配置更改(如屏幕旋转)时得以保留。ViewModel 会在界面控制器销毁和重新创建时保持数据不变,而 LiveData 可以通知观察者更新数据。
  • 避免内存泄漏:ViewModel 和 LiveData 的结合使用有助于避免内存泄漏问题。ViewModel 不持有对界面控制器的引用,而 LiveData 会自动处理观察者的生命周期,从而避免因观察者未及时取消注册而导致的内存泄漏。
  • 数据更新通知:LiveData 可以实现数据的观察和更新,当数据发生变化时,LiveData 会通知所有观察者进行相应的更新操作。这样可以确保界面与数据保持同步,提升用户体验。
  • 简化异步操作:LiveData 可以结合 ViewModel 使用,帮助简化异步操作和数据加载过程。可以在 ViewModel 中处理异步操作,然后通过 LiveData 将结果传递给观察者,从而实现响应式编程。
  • 解耦数据和界面:ViewModel 和 LiveData 的结合使用有助于将数据逻辑与用户界面分离,使代码更易于维护和测试。ViewModel 中的数据可以供多个界面控制器共享,避免重复加载数据。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java中如何判断一个数是不是素数(质数)

相关概念 质数就是大于1的自然数字中&#xff0c;只能被1和它自己整除的数。 题目 求101~200之间的质素的个数 代码实现 判断一个数是不是质数 for (int j 2; j < i; j) {if(i % j 0){flag false;break;}}if(flag){System.out.println("当前数字是质数");…

Unity 性能优化之遮挡剔除(Occlusion Culling)(六)

提示&#xff1a;仅供参考&#xff0c;有误之处&#xff0c;麻烦大佬指出&#xff0c;不胜感激&#xff01; 文章目录 前言一、遮挡剔除是什么&#xff1f;二、静态遮挡剔除的使用步骤1.标记为遮挡剔除对象2.创建Occlusion Area组件3.烘焙4.Occlusion窗口Bake的参数Smallest Oc…

电子书制作神器,简单操作

​随着数字化时代的到来&#xff0c;电子书籍越来越受到人们的喜爱。而一款优秀的电子翻页书制作软件&#xff0c;则能够帮助你轻松制作出专业级的电子书&#xff0c;让你的阅读体验更加丰富多彩。 今天&#xff0c;我们就来为大家推荐一款优秀的电子翻页书制作软件——FLBOOK在…

全球260多个国家的年通货膨胀率数据集(1960-2021年)

01、数据简介 全球年通货膨胀率是指全球范围内&#xff0c;在一年时间内&#xff0c;物价普遍上涨的比率。这种上涨可能是由于货币过度供应、需求过热、成本上升等原因导致的。通货膨胀率是衡量一个国家或地区经济状况和物价水平的重要指标&#xff0c;通常以消费者价格指数&a…

Flutter笔记:Widgets Easier组件库(12)使用消息吐丝(Notify Toasts)

Flutter笔记 Widgets Easier组件库&#xff08;12&#xff09;使用消息吐丝&#xff08;Notify Toasts&#xff09; - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 29114848416…

如何全面规避医疗数据安全风险?“一中心三大管控域”打开新思路!

作为医院的核心基础设施&#xff0c;数据库已然演变成了一种具有“资产”属性的重要元素。而随着不断变化的医疗业务场景和日趋严格的合规性要求&#xff0c;如何让安全全方位贯穿医疗数据的生命周期&#xff0c;是一项系统性的建设工作&#xff0c;难点诸多。 基于多年的数据…

Vue前端环境准备

vue-cli Vue-cli是Vue官方提供的脚手架&#xff0c;用于快速生成一个Vue项目模板 提供功能&#xff1a; 统一的目录结构 本地调试 热部署 单元测试 集成打包上线 依赖环境&#xff1a;NodeJs 安装NodeJs与Vue-Cli 1、安装nodejs&#xff08;已经安装就不用了&#xff09; node-…

Linux字符设备驱动(二) - 与设备驱动模型的关系

一&#xff0c;从/dev目录说起 从事Linux嵌入式驱动开发的人&#xff0c;都很熟悉下面的一些基础知识&#xff0c; 比如&#xff0c;对于一个char类型的设备&#xff0c;我想对其进行read wirte 和ioctl操作&#xff0c;那么&#xff1a; 1&#xff09;我们通常会在内核驱动中…

tomcat-以服务的方式重启tomcat

背景 双击tomcat的bin目录下面的startup.bat&#xff0c;会留下一个cmd的窗口&#xff0c;很不优雅 使用service服务的方式启动&#xff0c;并且设置为自动启动 找到tomcat的bin目录输入cmd&#xff0c;按Enter&#xff0c;进入命令行界面。执行“service.bat install” 。&…

文件删了,回收站清空了怎么恢复?文件恢复软件一览

在日常生活和工作中&#xff0c;我们常常会遇到误删除文件的情况&#xff0c;有时甚至会因为清空了回收站而无法找回这些文件。这些文件可能包含重要的工作数据、个人照片或其他珍贵的回忆。那么&#xff0c;在这种情况下&#xff0c;我们该如何恢复这些被删除且清空回收站的文…

【项目分享】用 Python 写一个桌面倒计日程序!

事情是这样的&#xff0c;我们班主任想委托我做一个程序&#xff0c;能显示还有几天考试。我立即理解了这个意思&#xff0c;接下了这个项目。 话不多说&#xff0c;来看看这个项目吧—— 项目简介 仓库地址&#xff1a;https://gitee.com/yaoqx/desktop-countdown-day 这是 …

为何美国多IP服务器搭建蜘蛛池SEO更经济?

为何美国多IP服务器搭建蜘蛛池SEO更经济? 随着网络时代的不断演进&#xff0c;搜索引擎优化(SEO)已经成为企业和个人提升网站曝光度的必经之路。在这个过程中&#xff0c;蜘蛛池(Spider Pool)服务被广泛应用。但是有趣的是&#xff0c;美国多IP服务器搭建蜘蛛池SEO服务却相对…

计算机网络-ICMPv6基础概念

前面我们学习了IPv6的基础概念以及IPv6地址的格式与分类&#xff0c;在IPv4中我们通过ARP、广播、ICMP进行地址冲突检测、网络连通性&#xff0c;但是在IPv6中是没有广播和ARP的&#xff0c;都是通过ICMPv6来实现其功能&#xff0c;所以这里我们需要了解下ICMPv6。 一、ICMP协议…

一个基于ComfuUI Api的 AIGC自动绘画实现方案

工作流程图 基本原理已经弄通&#xff0c;下一步要开始编码搬砖了。整个自动绘画的流程如下&#xff0c;暂就不整高深U什么L了&#xff0c;写个简单明了能容易看懂的流程图。UI借用了下墨刀里的AI绘画公开原型 部署节点 整个系统的后端服务典型部署需要3类节点 Aigc Server&…

mac/windows下安装docker,minikube

1、安装docker Get Started | Docker 下载安装docker 就行 启动后&#xff0c;就可以正常操作docker了 使用docker -v 验证是否成功就行 2、安装minikube&#xff0c;是基于docker-desktop的 2.1、点击设置 2.2、选中安装&#xff0c;这个可能需要一点时间 这样安装后&…

【研发管理】产品经理知识体系-组合管理

导读&#xff1a;新产品开发的组合管理是一个重要的过程&#xff0c;它涉及到对一系列新产品开发项目进行策略性选择、优先级排序、资源分配和监控。这个过程旨在确保企业能够最大化地利用有限的资源&#xff0c;以实现其战略目标。 目录 1、组合管理、五大目标 2、组合管理的…

OpenCV的周期性噪声去除滤波器(70)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇:OpenCV如何通过梯度结构张量进行各向异性图像分割(69) 下一篇 :OpenCV如何为我们的应用程序添加跟踪栏(71) 目录 目标 理论 如何消除傅里叶域中的周期性噪声&#xff1f; 源代码 解释 结果 目…

详解基于 RAG 的 txt2sql 全过程

前文 本文使用通义千问大模型和 ChromaDB 向量数据库来实现一个完整的 text2sql 的项目&#xff0c;并基于实际的业务进行效果的展示。 准备 在进行项目之前需要准备下面主要的内容&#xff1a; python 环境通义千问 qwen-max 模型的 api-keyChromaDB 向量数据库acge_text_…

Sharding Capital: 为什么投资全链流动性基础设施 Entangle ?

写在前面&#xff1a;Entangle 项目的名称取自于量子纠缠(Quantum entanglement)&#xff0c;体现了项目对于构建连接、关联和互通的愿景。就像量子纠缠将不同的粒子联系在一起&#xff0c;Entangle 旨在通过其跨链流动性和合成衍生品的解决方案将不同的区块链网络连接在一起&a…

django设计模式理解FBV和CBV

在 Web 开发中&#xff0c;FBV&#xff08;Function-Based Views&#xff09;和 CBV&#xff08;Class-Based Views&#xff09;是两种常见的视图设计模式&#xff0c;用于处理 HTTP 请求并生成相应的响应。下面是它们的简要解释&#xff1a; Function-Based Views (FBV) 在 …