当前位置: 首页 > news >正文

Android学习总结之类LiveData与ViewModel关系篇

1. ViewModel 和 LiveData 的强依赖关系

ViewModel 和 LiveData 虽非强依赖,但在 Android 架构中常紧密协作,这基于它们的设计理念和优势互补:

  • 数据与 UI 分离:ViewModel 的主要职责是存储和管理与 UI 相关的数据,而 LiveData 是一种可观察的数据持有者类。ViewModel 可以持有 LiveData 对象,将数据的变化通过 LiveData 通知给 UI 层,这样 UI 层只需要观察 LiveData 的变化,而不需要直接操作数据,实现了数据和 UI 的分离。
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;public class MyViewModel extends ViewModel {private MutableLiveData<String> data = new MutableLiveData<>();public LiveData<String> getData() {return data;}public void setData(String newData) {data.setValue(newData);}
}

ViewModel 持有 LiveData 对象,将数据的存储和更新逻辑封装在 ViewModel 中,而 UI 层只需要观察 LiveData 的变化,通过调用 getData() 方法获取 LiveData 对象并进行观察,实现了数据和 UI 的分离。 

  • 生命周期感知:LiveData 具有生命周期感知能力,它可以自动感知 Activity 或 Fragment 的生命周期状态。当 Activity 或 Fragment 处于活跃状态(STARTED 或 RESUMED)时,LiveData 会将数据的变化通知给观察者;当 Activity 或 Fragment 处于非活跃状态时,LiveData 会暂停通知,直到再次进入活跃状态。ViewModel 的生命周期与 Activity 或 Fragment 的生命周期绑定,当 Activity 或 Fragment 销毁时,ViewModel 才会被销毁。将 LiveData 放在 ViewModel 中,可以确保 LiveData 的生命周期与 ViewModel 一致,避免内存泄漏。
// LiveData.java
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {assertMainThread("observe");if (owner.getLifecycle().getCurrentState() == DESTROYED) {// ignorereturn;}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 方法中,会创建一个 LifecycleBoundObserver 对象,它实现了 LifecycleEventObserver 接口,会监听 LifecycleOwner 的生命周期变化。当 LifecycleOwner 的状态变为 DESTROYED 时,LifecycleBoundObserver 会自动移除观察者,避免内存泄漏。

而 ViewModel 的生命周期与 Activity 或 Fragment 绑定,当 Activity 或 Fragment 销毁时,ViewModelStore 会调用 ViewModel 的 clear 方法:

// ViewModel.java
protected void onCleared() {
}// ViewModelStore.java
public final void clear() {for (ViewModel vm : mMap.values()) {vm.clear();}mMap.clear();
}

将 LiveData 放在 ViewModel 中,可以确保 LiveData 的生命周期与 ViewModel 一致,因为 ViewModel 会在 Activity 或 Fragment 销毁时才被销毁,保证了数据的一致性和内存的安全。

2. 把 LiveData 放在普通类里面是否可行

可以把 LiveData 放在普通类里,但会失去很多架构优势:

  • 生命周期管理缺失:普通类没有生命周期感知能力,若 LiveData 置于其中,无法自动响应 Activity 或 Fragment 的生命周期变化。例如,在 Activity 销毁后,LiveData 可能仍在发送数据更新,这会导致内存泄漏或出现空指针异常。
// LiveData.java
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {// ...owner.getLifecycle().addObserver(wrapper);// ...
}

如果将 LiveData 放在普通类中,由于普通类无法提供 Lifecycle 对象,LiveData 就无法感知生命周期的变化,当 Activity 或 Fragment 销毁时,LiveData 无法自动移除观察者,可能会导致内存泄漏。 

  • 数据持久化与配置变更处理不便ViewModel 在配置变更时可以保留数据,这是因为 ViewModelStore 会在 Activity 或 Fragment 重建时保留 ViewModel 实例。而普通类没有这样的机制,在配置变更时,普通类的实例会被销毁和重建,数据也会丢失。

3. LiveData 是否需要依赖 ViewModel 来取消订阅

LiveData 不需要依赖 ViewModel 来取消订阅,它自身具备生命周期感知能力,能自动处理订阅和取消订阅:

  • 自动取消订阅:当观察者的生命周期处于 DESTROYED 状态时,LiveData 会自动移除该观察者,避免内存泄漏。例如,当 Activity 或 Fragment 销毁时,LiveData 会自动取消与之关联的所有观察者的订阅。
  • 不依赖 ViewModel:即使 LiveData 不放在 ViewModel 中,只要观察者是通过 LifecycleOwner(如 Activity 或 Fragment)注册的,LiveData 就能自动管理订阅的生命周期。
// LiveData.java#LifecycleBoundObserver
@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();}
}

在 onStateChanged 方法中,如果 LifecycleOwner 的状态为 DESTROYED,会调用 removeObserver 方法移除观察者,实现自动取消订阅。 

4. 一个 ViewModel 在 Fragment 销毁时执行哪些方法

在 Fragment 销毁时,ViewModel 本身不会执行特定的方法,但它会经历生命周期的结束阶段:

  • ViewModel 生命周期结束:当 Fragment 销毁时,ViewModel 会被保留,直到其宿主 Activity 销毁。如果 Fragment 是 Activity 的一部分,只有当 Activity 销毁时,ViewModel 才会被销毁。在这个过程中,ViewModel 没有像 Fragment 那样有明确的销毁方法回调,但它会被垃圾回收机制回收其占用的内存。
  • 数据清理(可选):如果在 ViewModel 中有一些需要手动清理的资源(如网络请求、数据库连接等),可以在 ViewModel 中实现 onCleared() 方法,在该方法中进行资源的释放操作。例如:
// ViewModel.java
public final void clear() {mCleared = true;if (mBagOfTags != null) {synchronized (mBagOfTags) {for (Object value : mBagOfTags.values()) {// see comment for the similar call in setTagIfAbsentcloseWithRuntimeException(value);}}}onCleared();
}

clear 方法会标记 ViewModel 为已清理状态,然后调用 onCleared 方法。onCleared 方法是一个空方法,我们可以在 ViewModel 的子类中重写该方法,进行资源的清理操作,例如取消网络请求、关闭数据库连接等。 

5. 解释一下 LiveData 以及它是如何感知生命周期的

解释 LiveData

LiveData 是一种可观察的数据持有者类,它具有以下特点:

  • 生命周期感知:LiveData 可以感知 Activity、Fragment 或 Service 的生命周期,确保只有在生命周期处于活跃状态时才会通知观察者数据的变化。
  • 自动取消订阅:当观察者的生命周期处于 DESTROYED 状态时,LiveData 会自动移除该观察者,避免内存泄漏。
  • 数据一致性:LiveData 会在数据发生变化时通知所有活跃的观察者,确保 UI 始终显示最新的数据。
// LiveData.java
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =new SafeIterableMap<>();protected void setValue(T value) {assertMainThread("setValue");mVersion++;mData = value;dispatchingValue(null);
}private 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;}if (observer.mLastVersion >= mVersion) {return;}observer.mLastVersion = mVersion;//noinspection uncheckedobserver.mObserver.onChanged((T) mData);
}

当调用 setValue 方法时,会更新数据版本号,然后调用 dispatchingValue 方法通知所有观察者。在 considerNotify 方法中,会检查观察者的活跃状态和数据版本号,确保只有活跃的观察者且数据版本号有更新时才会通知观察者。 

如何感知生命周期

LiveData 通过 LifecycleBoundObserver 来感知 LifecycleOwner 的生命周期变化。LifecycleBoundObserver 实现了 LifecycleEventObserver 接口,当 LifecycleOwner 的状态发生变化时,onStateChanged 方法会被调用:

// LiveData.java#LifecycleBoundObserver
@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();}
}

在 onStateChanged 方法中,会根据 LifecycleOwner 的当前状态进行相应的处理。如果状态为 DESTROYED,会移除观察者;如果状态发生变化,会调用 activeStateChanged 方法更新观察者的活跃状态。

面试扩展追问:

1. ViewModel 和 LiveData 的强依赖关系

真题 1(字节跳动)
"ViewModel 如何保证在 Activity 重建时数据不丢失?请结合源码解释。"

解析
ViewModel 的数据持久化机制基于 ViewModelStore 和 NonConfigurationInstances

  1. Activity 重建流程
    当屏幕旋转等配置变更发生时,Activity 会经历销毁重建,但系统会通过 onRetainNonConfigurationInstance() 保存一个 NonConfigurationInstances 对象,其中包含 ViewModelStore

  2. ViewModelStore 缓存
    ViewModel 实例由 ViewModelStore 管理(内部是一个 Map),Activity 重建后会从 NonConfigurationInstances 中恢复该 Store,从而复用之前的 ViewModel。

  3. 源码验证

    // Activity.java
    final Object onRetainNonConfigurationInstance() {Object custom = onRetainCustomNonConfigurationInstance();ViewModelStore viewModelStore = mViewModelStore;// ...NonConfigurationInstances nci = new NonConfigurationInstances();nci.custom = custom;nci.viewModelStore = viewModelStore; // 保存 ViewModelStorereturn nci;
    }
    

真题 2(阿里)
"LiveData 如何保证数据更新时只通知活跃的观察者?"

解析
LiveData 通过状态管理和版本控制实现精准通知:

  1. 活跃状态判断
    观察者的活跃状态由 shouldBeActive() 方法决定,必须处于 STARTED 或 RESUMED 状态。

  2. 版本号机制
    每次调用 setValue() 时,LiveData 的 mVersion 会递增。观察者记录自己的 mLastVersion,只有当 mVersion > mLastVersion 时才会触发更新。

  3. 源码验证

    // LiveData.java
    private void considerNotify(ObserverWrapper observer) {if (!observer.mActive) return; // 非活跃状态不通知if (observer.mLastVersion >= mVersion) return; // 版本相同不通知observer.mLastVersion = mVersion;observer.mObserver.onChanged((T) mData); // 通知更新
    }
    

2. 把 LiveData 放在普通类里面是否可行

真题 3(腾讯)
"将 LiveData 放在普通 POJO 类中会有什么问题?如何解决?"

解析
存在三大风险:

  1. 内存泄漏风险
    普通类无法感知生命周期,当 Activity/Fragment 销毁后,LiveData 可能仍持有其引用,导致无法被回收。

  2. 空指针异常
    若 Activity 已销毁,LiveData 仍发送数据更新,可能触发空指针(如更新 UI 操作)。

  3. 数据一致性问题
    配置变更时,普通类实例会重建,导致数据丢失(而 ViewModel 会被保留)。

解决方案

  • 使用 viewModelScope 或 lifecycleScope 绑定协程生命周期。
  • 手动管理观察者订阅(在 onDestroy() 中调用 removeObserver())。
  • 优先使用 ViewModel 作为 LiveData 的宿主。

3. LiveData 是否需要依赖 ViewModel 来取消订阅

真题 4(美团)
"LiveData 自动取消订阅的原理是什么?如果手动调用 removeObserver() 会发生什么?"

解析

  1. 自动取消订阅原理
    LifecycleBoundObserver 实现了 LifecycleEventObserver,当宿主状态变为 DESTROYED 时,会自动调用 removeObserver()

  2. 手动取消订阅场景

    • 当需要提前释放资源时(如 Fragment 隐藏但未销毁)。
    • 非 LifecycleOwner 作为观察者时(如普通 Java 对象)。
  3. 源码验证

    // LiveData.java
    class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {@Overridepublic void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {if (source.getLifecycle().getCurrentState() == DESTROYED) {removeObserver(mObserver); // 自动移除return;}}
    }
    

4. 一个 ViewModel 在 Fragment 销毁时执行哪些方法

真题 5(百度)
"Fragment 销毁时,ViewModel 的 onCleared() 一定会被调用吗?为什么?"

解析
不一定!关键点在于 Fragment 的生命周期与 ViewModel 的绑定逻辑

  1. 普通 Fragment 销毁
    若 Fragment 被移除但 Activity 未销毁,ViewModel 不会被清除(onCleared() 不调用)。因为 ViewModel 的生命周期绑定到 Activity。

  2. Activity 销毁
    当 Activity 销毁时,ViewModelStore 会调用 clear(),触发 onCleared()

  3. 内存不足场景
    系统在后台杀死 Activity 时,ViewModel 会被保留(在 onRetainNonConfigurationInstance() 中),但进程重启后 ViewModel 会重新创建,onCleared() 不会被调用。

5. 解释一下 LiveData 以及它是如何感知生命周期的

真题 6(字节跳动)
"如何自定义一个具有生命周期感知能力的 LiveData?"

解析
自定义 LiveData 需要继承 LiveData<T> 并实现生命周期监听:

public class MyLiveData extends LiveData<String> {private MyDataSource dataSource;public MyLiveData(MyDataSource dataSource) {this.dataSource = dataSource;}@Overrideprotected void onActive() {// 当第一个活跃观察者出现时调用dataSource.registerListener(this::setValue);}@Overrideprotected void onInactive() {// 当没有活跃观察者时调用dataSource.unregisterListener();}
}

关键点

  • onActive():开始监听数据源(如网络请求、数据库)。
  • onInactive():停止监听,释放资源。
  • 生命周期状态由 LiveData 内部的 mActiveCount 控制。

6. 高阶真题(常考设计题)

真题 7(阿里)
"如何实现一个支持黏性事件的 LiveData?"

解析
黏性事件指观察者订阅时能收到最近一次发送的数据。实现方案:

import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.LifecycleOwner;/*** 黏性 LiveData 实现类(支持新订阅者获取最新数据)* 核心功能:当新观察者订阅时,立即获取最新一次发送的数据(即使在订阅前数据已更新)* 设计原理:通过版本号机制记录数据更新次数,确保新订阅者能收到订阅前的最新数据,同时避免重复通知旧订阅者*/
public class StickyLiveData<T> extends LiveData<T> {// 缓存最新的数据(用于向新订阅者发送黏性事件)private T stickyData;// 数据版本号(每次数据更新时递增,用于判断数据是否为新的)private int version = 0;/*** 重写数据更新方法,增加版本号管理和数据缓存* @param value 新数据*/@Overridepublic void setValue(T value) {// 1. 版本号自增(确保每次更新都是一个新的版本)version++;// 2. 缓存最新数据(供新订阅者获取)stickyData = value;// 3. 调用父类方法触发数据更新通知(走 LiveData 原生通知流程)super.setValue(value);}/*** 重写订阅方法,使用自定义包装类记录订阅时的版本号* @param owner 生命周期宿主(如 Activity/Fragment)* @param observer 原始观察者*/@Overridepublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {// 创建自定义观察者包装类,传入原始观察者和当前版本号(订阅时的版本)StickyObserver stickyObserver = new StickyObserver(observer, version);// 调用父类方法注册包装后的观察者(利用 LiveData 原生的生命周期管理)super.observe(owner, stickyObserver);}/*** 自定义观察者包装类(核心逻辑:通过版本号过滤数据更新通知)*/private class StickyObserver implements Observer<T> {// 持有原始观察者(最终由它触发数据变化回调)private final Observer<? super T> observer;// 记录订阅时的版本号(用于判断后续收到的数据是否是新的)private int lastVersion;/*** 构造方法:初始化原始观察者和订阅时的版本号* @param observer 原始观察者* @param version 订阅时的当前版本号(即数据更新次数)*/public StickyObserver(Observer<? super T> observer, int version) {this.observer = observer;this.lastVersion = version; // 保存订阅时的版本(初始版本)}/*** 数据变化回调(重写 LiveData 的通知逻辑)* @param t 新数据*/@Overridepublic void onChanged(T t) {// 1. 版本号比较:仅当当前数据版本号 > 订阅时的版本号时触发通知//    - 新订阅者:首次收到数据时,lastVersion 是订阅前的版本,当前 version 已自增,会触发通知//    - 旧订阅者:若数据版本未更新(version 不变),不会重复通知if (lastVersion < version) {// 2. 更新本地记录的版本号(确保后续通知只处理更高版本的数据)lastVersion = version;// 3. 触发原始观察者的回调(传递最新数据)observer.onChanged(t);}// 注意:若版本号未变化(如重复调用 setValue 相同数据),不会触发通知,保持 LiveData 原生行为}}
}

关键逻辑说明:

  1. 版本号机制(核心设计):

    • version 每次调用 setValue() 时自增,确保每次数据更新都是一个唯一的 “版本”。
    • 新订阅者注册时,StickyObserver 记录当前 version(即订阅前的最新版本)。
    • 当数据更新时(version 变大),只有 lastVersion < version 的观察者会收到通知,确保新订阅者能获取订阅前的最新数据(黏性事件),而旧订阅者不会重复接收已处理的旧数据。
  2. 数据缓存

    • stickyData 存储最新数据,配合版本号机制,确保新订阅者首次回调时能获取到最新值(即使在订阅前数据已更新)。
    • 注意:stickyData 并非必须(父类 LiveData 已通过 mData 存储数据),此处主要为逻辑清晰而显式声明,实际可直接使用父类的 mData,但需通过反射访问(不推荐),因此独立缓存更简洁。
  3. 生命周期感知

    • 继承自 LiveData,天然支持 LifecycleOwner(如 Activity/Fragment)的生命周期管理,自动处理订阅和取消订阅,避免内存泄漏。
    • StickyObserver 复用父类的 observe() 方法,确保黏性逻辑与原生生命周期机制兼容。
  4. 线程安全

    • setValue() 要求在主线程调用(遵循 LiveData 规范),postValue() 可在子线程调用,内部会切换到主线程通知观察者,确保线程安全。

相关文章:

Android学习总结之类LiveData与ViewModel关系篇

1. ViewModel 和 LiveData 的强依赖关系 ViewModel 和 LiveData 虽非强依赖&#xff0c;但在 Android 架构中常紧密协作&#xff0c;这基于它们的设计理念和优势互补&#xff1a; 数据与 UI 分离&#xff1a;ViewModel 的主要职责是存储和管理与 UI 相关的数据&#xff0c;而…...

GaussDB 实例 gsql 连接方式详解

GaussDB 实例 gsql 连接方式详解 GaussDB 是华为云推出的分布式关系型数据库服务&#xff0c;支持多种数据库引擎&#xff08;如 MySQL、PostgreSQL、SQL Server 等&#xff09;。gsql 是 GaussDB 提供的命令行客户端工具&#xff0c;用于连接和管理数据库实例。本文将详细介绍…...

智能体制作学习笔记2——情感客服

02 案例1-情感客服_哔哩哔哩_bilibili 目录 一、AI对视频内容总结 二、选择可代替视频总结的方案 三、豆包AI插件安装 四、通义 五、情感客服智能体制作 &#xff08;一&#xff09;注册 (二&#xff09;进入工作空间 &#xff08;三&#xff09;创建智能体 &#xff08;…...

部署GraphRAG配置Neo4j实现知识图谱可视化【踩坑经历】

文章目录 概要部署graphrag(一)使用conda创建虚拟环境(前提已经安装好anaconda)(二)部署graphrag 部署neo4jgraphrag生成的知识图谱导入neo4j踩坑经历1.graphrag执行graphrag index --root ./ragtest命令报错2.neo4j没有Relationship types 概要 在本地部署GraphRag&#xff0…...

跨域的几种方案

因为浏览器出于安全考虑&#xff0c;有同源策略。也就是说&#xff0c;如果协议、域名、端口有一个不同就是跨域&#xff0c;Ajax 请求会失败。 我们可以通过以下几种常用方法解决跨域的问题 JSONP JSONP 的原理很简单&#xff0c;就是利用 <script> 标签没有跨域限制…...

5 WPF中的application对象介绍

WPF Application 类提供了一系列生命周期事件,了解它们的触发顺序对于应用程序开发非常重要。以下是主要事件的触发顺序 1. 主要事件顺序 Startup - 应用程序启动时触发 这是第一个触发的事件 适合在此处初始化应用程序级资源 可以在此取消启动(通过设置e.Cancel = true) Act…...

Nexus首次亮相迪拜 TOKEN2049:以“手机 + 钱包 + 公链 + RWA”生态系统引领未来区块链基建

迪拜&#xff0c;2025年5月—— 全球 Web3 基础设施创新平台 Nexus&#xff0c;在本年度迪拜 TOKEN2049 全球峰会 上完成了其主网与全生态系统的首次国际公开亮相。此次参会不仅展示了 Nexus 的国际生态布局&#xff0c;更标志着其迈出了全球化战略关键一步。凭借对现实世界资产…...

C++ 套接字函数详细介绍

目录 头文件1. 套接字创建与配置2. 绑定地址与端口3. 连接建立4. 数据传输5. 套接字选项6. 地址转换7. 套接字关闭8. 其他实用函数 C 套接字函数详细介绍 套接字(Socket)是网络通信的基本端点&#xff0c;C中通常使用BSD套接字API进行网络编程。以下是主要的套接字相关函数及其…...

WordPress 和 GPL – 您需要了解的一切

如果您使用 WordPress&#xff0c;GPL 对您来说应该很重要&#xff0c;您也应该了解它。查看有关 WordPress 和 GPL 的最全面指南。 您可能听说过 GPL&#xff08;通常被称为 WordPress 的权利法案&#xff09;&#xff0c;但很可能并不完全了解它。这是有道理的–这是一个复杂…...

机器人示教操作

机器人基础操作 **ES机器人试教操作知识** **1. 视角移动** **1.1 基础模式** - 关节轴控制&#xff1a;通过关节1至关节6实现单轴正反转移动 - 直线移动&#xff1a;通过X/Y/Z坐标轴沿指定方向直线移动 - 旋转移动&#xff1a;通过RX/RY/RZ坐标轴绕指定轴旋转 **1.2 步进模式…...

【python】UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte 0xb2

报错 C:\Users\zhangbin\AppData\Local\Programs\Python\Python310\python.exe D:\XTRANS\cuda\03-graph-db\04-cmkg\pdf2zh-v1.9.9-with-assets-win64\pdf2zh\gui.py Traceback (most recent call last): File “D:\XTRANS\cuda\03-graph-db\04-cmkg\pdf2zh-v1.9.9-with-asset…...

[python] python静态方法,类方法,实例方法实现及其区别

一 静态方法 格式: 使用 staticmethmod 装饰器修饰 应用: 某个方法既不需要使用实例属性也不需要使用类属性时,就可以考虑使用静态方法 注意: 静态方法与类无关,可以被转换成函数使用,属于类本身 1.1 经典示例 创建一个与日期相关的辅助函数,这些函数不需要访问或修改类的…...

Kite AI 自动机器人部署教程

最近比较火的AI赛道&#xff0c;每日自动对话训练AI&#xff0c;赚积分 一个个用于 Kite AI 平台的自动交互机器人&#xff0c;支持多钱包和代理。 登记 注册链接 &#x1f31f; 功能 多钱包支持&#xff08;手动输入或基于文件&#xff09; 代理支持&#xff08;HTTP/HTTP…...

50. Pow(x, n)

50. Pow(x, n) 分治法的基本思想是将一个大问题分解成若干个相同或相似的小问题&#xff0c;递归地解决这些小问题&#xff0c;然后将这些小问题的解合并起来得到原问题的解。 class Solution:def myPow(self, x: float, n: int) -> float:# 内部定义了一个嵌套的辅助函数…...

Go 语言 sqlx 库使用:对 MySQL 增删改查

MySQL 作为目前最流行的开源关系型数据库&#xff0c;其 SQL 语法体系已形成行业标准&#xff0c;相关知识体系庞大且成熟&#xff0c;本文不再对 SQL 基础进行详细展开&#xff0c;建议尚未掌握的读者先行系统学习。本文聚焦于如何使用 Go 语言进行 MySQL 数据库操作&#xff…...

反射, 注解, 动态代理

文章目录 单元测试什么是单元测试咱们之前是如何进行单元测试的&#xff1f; 有啥问题 &#xff1f;现在使用方法进行测试优点Junit单元测试的使用步骤删除不需要的jar包总结 反射认识反射、获取类什么是反射反射具体学什么&#xff1f;反射第一步&#xff1a;或者Class对象 获…...

继续预训练 LLM ——数据筛选的思路

GPT生成数据微调qwen-2.5多模态模型实战项目 作者&#xff1a;柠檬养乐多 原文地址&#xff1a;https://zhuanlan.zhihu.com/p/30645776656 qwen2.5-vl是阿里通义实验室推出的qwen系列最新多模态大模型&#xff0c;在许多指标上已经超过或接近了gpt-4o。更为方便的是&#xff0…...

深入解析 PostgreSQL 外部数据封装器(FDW)的 SELECT 查询执行机制

引言 PostgreSQL 中的外部数据封装器&#xff08;Foreign Data Wrapper, FDW&#xff09;是一种扩展&#xff0c;允许您像访问 PostgreSQL 数据库中的表一样&#xff0c;访问和操作存储在外部数据源中的数据。FDW 使 PostgreSQL 能够与多种数据存储系统&#xff08;包括关系型…...

数据库系统概论|第六章:关系数据理论—课程笔记2

前言 前文我们介绍了规划化的基本概念&#xff0c;同时引入了关于规范化的相关定义与基本概念&#xff0c;低一级范式的关系模式&#xff0c;通过模式分解&#xff0c;可以转换为若干个高一级范式的关系模式的集合&#xff0c;这种过程就叫规范化。本文将围绕范式展开讨论&…...

package-lock.json能否直接删除?

package-lock.json能否直接删除&#xff1f; package-lock.json 生成工具&#xff1a;由 npm 自动生成。 触发条件&#xff1a;当运行 npm install 时&#xff0c;如果不存在 package-lock.json&#xff0c;npm 会创建它&#xff1b;如果已存在&#xff0c;npm 会根据它精确安…...

Ubuntu磁盘空间分析:du命令及常用组合

1、du命令的作用 du&#xff08;Disk Usage&#xff09;是 Ubuntu 系统中用于查看目录或文件磁盘使用情况的命令&#xff0c;主要用于分析磁盘空间占用。 2、语法 du [选项] [目录/文件路径]常用选项 2.1、-h 以 KB、MB、GB 等人性化可读格式&#xff08;Human-readable&am…...

《数据库原理》部分习题解析1

《数据库原理》部分习题解析1 1. 名词解释 &#xff08;1&#xff09;关系&#xff08;2&#xff09;属性&#xff08;3&#xff09;域&#xff08;4&#xff09;元组&#xff08;5&#xff09;码&#xff08;6&#xff09;分量&#xff08;7&#xff09;关系模式 &#xff0…...

汇川Easy系列PLC数据值改变功能块(随动增益改变判断)

PLC值改变事件 值改变触发功能块 PLC值改变事件 值改变触发功能块(SCL ST完整源代码)-CSDN博客文章浏览阅读1.1k次。本文介绍了在PLC中处理值改变事件的方法,包括值改变触发功能块的实现,详细讲解了FB接口定义、ST代码,并提供了在博途平台上的实现。此外,还分享了如何利用…...

数据清洗的艺术:如何为AI模型准备高质量数据集?

数据清洗的艺术&#xff1a;如何为AI模型准备高质量数据集&#xff1f; 引言 在人工智能和机器学习领域&#xff0c;我们常常听到"垃圾进&#xff0c;垃圾出"(Garbage in, garbage out)这句格言。无论你的模型架构多么精妙&#xff0c;算法多么先进&#xff0c;如果…...

怎么查看当前vue项目,要求的node.js版本

怎么查看当前vue项目&#xff0c;要求的node.js版本 找到 package.json package-lock.json 搜索 node...

游戏引擎学习第278天:将实体存储移入世界区块

总结并为今天的内容做好铺垫 今天的内容是关于开发一个完整的实体系统&#xff0c;目标是让这个系统更加实际和有效。之前讨论了如何通过一个模拟区域来处理无限大的世界。最初&#xff0c;使用浮动点数而不是双精度浮点数来避免潜在的精度问题&#xff0c;因为一些平台&#…...

计算机组成与体系结构:缓存设计概述(Cache Design Overview)

目录 Block Placement&#xff08;块放置&#xff09; Block Identification&#xff08;块识别&#xff09; Block Replacement&#xff08;块替换&#xff09; Write Strategy&#xff08;写策略&#xff09; 总结&#xff1a; 高速缓存设计包括四个基础核心概念&#xf…...

在Linux中如何使用Kill(),向进程发送发送信号

kill()函数 #include <sys/types.h> #include <signal.h> int kill(pid_t pid, int sig); 函数参数和返回值含义如下: pid:参数 pid 为正数的情况下,用于指定接收此信号的进程 pid;除此之外,参数 pid 也可设置为 0 或-1 以及小于-1 等不同值,稍后给说明。 …...

ElasticSearch重启之后shard未分配问题的解决

以下是Elasticsearch重启后分片未分配问题的完整解决方案&#xff0c;结合典型故障场景与最新实践&#xff1a; 一、快速诊断定位 ‌检查集群状态 GET /_cluster/health?pretty # status为red/yellow时需关注unassigned_shards字段值 ‌ 2.查看未分配分片详情 …...

基于 Spring Boot 瑞吉外卖系统开发(十四)

基于 Spring Boot 瑞吉外卖系统开发&#xff08;十四&#xff09; 查询订单 在管理端的首页&#xff0c;单击左侧菜单栏中的“订单明细”&#xff0c;会在右侧打开订单明细页面。 请求路径&#xff1a;/order/page 请求方法&#xff1a;GET 参数&#xff1a;page pageSize …...

【软件测试】第二章·软件测试的基本概念

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a;&#x1f3c0;软件测试与软件项目管理_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 …...

部署安装gitlab-ce-17.9.7-ce.0.el8.x86_64.rpm

目录 ​编辑 实验环境 所需软件 实验开始 安装部署gitlab171.配置清华源仓库&#xff08;版本高的系统无需做&#xff09;vim /etc/yum.repos.d/gitlab-ce.repo 2.提前下载包dnf localinstall gitlab-ce-17.9.7-ce.0.el8.x86_64.rpm --rocklinux 3.修改配…...

2025五一杭州西湖三天游

2025五一杭州西湖三天游 文章目录 2025五一杭州西湖三天游一、前言二、杭州游玩记录三、杭州三日游小结四、杭州美食街1、河坊街2、胜利河美食街3、高银街4、中山南路美食街5、武林夜市6、啦喜街美食广场7、大兜路美食街 五、豆包推荐的杭州三日游攻略三天主要行程**第一天&…...

实验五:以太网UDP全协议栈的实现(通过远程实验系统)

文章目录 FPGA以太网:从ARP到UDP的完整协议栈一、引言二、核心模块详解1. ARP协议处理模块1.1 `arp_cache`:ARP缓存模块1.2 `arp_tx`:ARP请求与应答发送模块1.3 `arp_rx`:ARP接收与解析模块2. MAC层处理模块2.1 `mac_layer`:MAC层顶层模块2.2 `mac_tx_mode`:MAC发送模式选…...

现代计算机图形学Games101入门笔记(八)

三角形三点已经知道在uv的位置了&#xff0c;那三角形内部的点&#xff0c;怎么算。 先看A 任一点 面积比求 根据三点坐标属性差值出内部点的位置。 纹理太小了&#xff0c;映射处理方式&#xff0c;取邻近的Nearest感觉一格格的&#xff0c;取周围4个权重Bilinear,取4*4Bicubi…...

C语言学习之文件操作

经过前面的学习&#xff0c;我们已经基本掌握了如何去写一个C语言的代码了。但是在实际的项目中&#xff0c;我们不可能不需要文件去操作。因为如果没有文件&#xff0c;我们写的程序是存储在电脑的内存中的。如果程序推出&#xff0c;内存回收数据就随之丢失了。如果我们要对数…...

《AI大模型应知应会100篇》第63篇:AutoGPT 与 BabyAGI:自主代理框架探索

第63篇&#xff1a;AutoGPT 与 BabyAGI&#xff1a;自主代理框架探索 摘要 随着大语言模型&#xff08;LLM&#xff09;技术的不断演进&#xff0c;自主代理&#xff08;Autonomous Agent&#xff09; 正在成为 AI 应用的新范式。它不仅能够理解用户意图&#xff0c;还能自主规…...

使用大模型预测急性结石性疾病技术方案

目录 1. 数据预处理与特征工程伪代码 - 数据清洗与特征处理数据预处理流程图2. 大模型构建与训练伪代码 - 模型训练模型训练流程图3. 术前预测系统伪代码 - 术前风险评估术前预测流程图4. 术中实时调整系统伪代码 - 术中风险预警术中调整流程图5. 术后护理系统伪代码 - 并发症预…...

基于运动补偿的前景检测算法

这段代码实现了基于运动补偿的前景检测算法。 主要功能包括&#xff1a; 运动补偿模块&#xff1a;使用基于网格的 KLT 特征跟踪算法计算两帧之间的运动&#xff0c;然后通过单应性变换实现帧间运动补偿。前景检测模块&#xff1a;结合两帧运动补偿结果&#xff0c;通过帧间差…...

鸿蒙OSUniApp开发富文本编辑器组件#三方框架 #Uniapp

使用UniApp开发富文本编辑器组件 富文本编辑在各类应用中非常常见&#xff0c;无论是内容创作平台还是社交软件&#xff0c;都需要提供良好的富文本编辑体验。本文记录了我使用UniApp开发一个跨平台富文本编辑器组件的过程&#xff0c;希望对有类似需求的开发者有所启发。 背景…...

W5500使用SocketTool工具测试

W5500使用SocketTool工具测试 1、按“WINR” 2、输入“IPCONFIG”&#xff0c;得到计算机的IP地址&#xff0c;子网掩码和网关 3、设置W5500设备网络参数如下&#xff1a; 本地网关&#xff1a;192.168.1.1 本地子网掩码: 255.255.255.0 本地物理地址&#xff1a;0C 2…...

《Python星球日记》 第71天:命名实体识别(NER)与关系抽取

名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、命名实体识别&#xff08;NER&#xff09;基础1. 什么是命名实体识别&#…...

双向长短期记忆网络-BiLSTM

5月14日复盘 二、BiLSTM 1. 概述 双向长短期记忆网络&#xff08;Bi-directional Long Short-Term Memory&#xff0c;BiLSTM&#xff09;是一种扩展自长短期记忆网络&#xff08;LSTM&#xff09;的结构&#xff0c;旨在解决传统 LSTM 模型只能考虑到过去信息的问题。BiLST…...

CentOS7原有磁盘扩容实战记录(LVM非LVM)【针对GPT分区】

一、环境 二、命令及含义 fdisk ‌ ‌ fdisk‌是一个较老的分区表创建和管理工具&#xff0c;主要支持MBR&#xff08;Master Boot Record&#xff09;格式的分区表。MBR分区表支持的硬盘单个分区最大容量为2TB&#xff0c;最多可以有4个主分区。fdisk通过命令行界面进行操…...

如何在终端/命令行中把PDF的每一页转换成图片(PNG)

今天被对象安排了一个任务&#xff1a; 之前自己其实也有这个需要&#xff0c;但是吧&#xff0c;我懒&#xff1a;量少拖拽&#xff0c;量大就放弃。但这次躲不过去了&#xff0c;所以研究了一下有什么工具可以做到这个需求。 本文记录我这次发现的使用 XpdfReader 的方法。…...

【0415】Postgres内核 释放指定 memory context 中所有内存 ④

1. frees all memory (memory context) Postgres内核中由函数 AllocSetReset() 完成该功能。即 “释放给定set中分配的所有内存。” 它应当将所有已分配的chunks标记为已释放,但不一定需要归还set所拥有的全部资源。我们的实际实现是,除了“保留”块(“keeper” block)(…...

2025年Flutter初级工程师技能要求

在2025年&#xff0c;随着移动应用市场的持续增长和跨平台开发需求的不断增加&#xff0c;Flutter已经成为许多公司构建高性能、跨平台应用的首选框架。对于初入职场的Flutter初级工程师来说&#xff0c;掌握以下技能要求是必不可少的。这些技能不仅能够帮助你在工作中快速上手…...

AWS技术助力企业满足GDPR合规要求

GDPR(通用数据保护条例)作为欧盟严格的数据保护法规,给许多企业带来了合规挑战。本文将探讨如何利用AWS(亚马逊云服务)的相关技术来满足GDPR的核心要求,帮助企业实现数据保护合规。 一、GDPR核心要求概览 GDPR的主要目标是保护欧盟公民的个人数据和隐私权。其核心要求包括: 数…...

MVCC:数据库并发控制的利器

在并发环境下&#xff0c;数据库需要处理多个事务同时访问和修改数据的情况。为了保证数据的一致性和隔离性&#xff0c;数据库需要采用一些并发控制机制。MVCC (Multi-Version Concurrency Control&#xff0c;多版本并发控制) 就是一种常用的并发控制技术&#xff0c;它通过维…...

第二章、Isaaclab强化学习包装器(3):SKRL Wrapper

0 前言 官方文档&#xff1a;https://isaac-sim.github.io/IsaacLab/main/source/api/lab_rl/isaaclab_rl.html#module-isaaclab_rl.skrl https://skrl.readthedocs.io/en/latest/intro/getting_started.html 在本节中&#xff0c;您将学习如何使用 skrl 库的各种组件来创建强…...