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

Android学习总结之Glide自定义三级缓存(实战篇)

一、为什么需要三级缓存

内存缓存(Memory Cache)

内存缓存旨在快速显示刚浏览过的图片,例如在滑动列表时来回切换的图片。在 Glide 中,内存缓存使用 LruCache 算法(最近最少使用),能自动清理长时间未使用的图片,以此确保内存的合理利用。通常,内存缓存限制在手机可用内存的 15%。举例来说,若手机拥有 8GB 内存,内存缓存大约为 1.2GB。同时,为了进一步优化,图片会按屏幕尺寸进行压缩,比如原图为 2000px,而手机屏幕为 1000px,那么只存储 1000px 版本的图片。当内存缓存超出限制时,会自动清理超出部分的图片。

代码实现

import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.load.engine.cache.LruResourceCache;
import com.bumptech.glide.module.AppGlideModule;public class CustomGlideModule extends AppGlideModule {@Overridepublic void applyOptions(Context context, GlideBuilder builder) {// 获取设备的最大内存int maxMemory = (int) Runtime.getRuntime().maxMemory();// 计算内存缓存的大小,这里设置为最大内存的15%int memoryCacheSize = maxMemory / 1024 / 1024 * 15;// 创建LruResourceCache对象builder.setMemoryCache(new LruResourceCache(memoryCacheSize));}
}

磁盘缓存(Disk Cache)

磁盘缓存用于存储常用但当前不在内存中的图片,像用户经常访问的商品详情页图片。Glide 通过 DiskLruCache 将图片存储在手机硬盘上,总容量一般设置为 100MB,并且优先存储高质量图片。为了优化存储,图片按 URL 哈希值命名文件,这样可以避免重复存储相同图片。同时,对于超过 7 天未使用的图片,会自动进行清理。

代码实现

import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.load.engine.cache.DiskLruCacheFactory;
import com.bumptech.glide.module.AppGlideModule;public class CustomDiskCacheGlideModule extends AppGlideModule {@Overridepublic void applyOptions(Context context, GlideBuilder builder) {// 设置磁盘缓存的路径String diskCachePath = context.getCacheDir().getPath() + "/glide_cache";// 设置磁盘缓存的大小为100MBint diskCacheSize = 1024 * 1024 * 100;// 创建DiskLruCacheFactory对象builder.setDiskCache(new DiskLruCacheFactory(diskCachePath, diskCacheSize));}
}

网络缓存(Network Cache)

网络缓存的作用是避免重复从服务器下载相同图片,这需要结合 HTTP 缓存头来实现。Glide 借助 OkHttp 的缓存机制,将图片存储在路由器或基站缓存中,总容量设置为 50MB,优先存储高频访问的图片。通过根据 HTTP 的 Cache-Control 头设置缓存时间(例如设置为 1 天),以及在图片 URL 中添加版本号(如 image_v2.jpg),当版本更新时强制重新下载,从而实现高效的网络缓存管理。

代码实现

import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.module.AppGlideModule;
import okhttp3.Cache;
import okhttp3.OkHttpClient;import java.io.InputStream;public class CustomNetworkCacheGlideModule extends AppGlideModule {@Overridepublic void registerComponents(Context context, Glide glide, Registry registry) {// 设置网络缓存的路径Cache cache = new Cache(context.getCacheDir(), 1024 * 1024 * 50);// 创建OkHttpClient对象并设置缓存OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();// 注册OkHttpUrlLoader,让Glide使用OkHttp进行网络请求registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client));}
}

整合代码: 

import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.load.engine.cache.LruResourceCache;
import com.bumptech.glide.module.AppGlideModule;/*** 自定义Glide内存缓存配置* 通过LruCache算法实现最近最少使用的图片自动回收*/
public class CustomGlideModule extends AppGlideModule {@Overridepublic void applyOptions(Context context, GlideBuilder builder) {// 获取应用可使用的最大内存(单位:字节)int maxMemory = (int) Runtime.getRuntime().maxMemory();// 计算内存缓存大小(15%的可用内存)int memoryCacheSize = maxMemory / 1024 / 1024 * 15;// 创建LruResourceCache并设置缓存大小builder.setMemoryCache(new LruResourceCache(memoryCacheSize));}
}import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.load.engine.cache.DiskLruCacheFactory;
import com.bumptech.glide.module.AppGlideModule;/*** 自定义Glide磁盘缓存配置* 使用DiskLruCache将图片持久化到本地存储*/
public class CustomDiskCacheGlideModule extends AppGlideModule {@Overridepublic void applyOptions(Context context, GlideBuilder builder) {// 设置磁盘缓存路径(应用缓存目录下的glide_cache文件夹)String diskCachePath = context.getCacheDir().getPath() + "/glide_cache";// 设置磁盘缓存大小(100MB)int diskCacheSize = 1024 * 1024 * 100;// 创建DiskLruCache工厂并设置路径和大小builder.setDiskCache(new DiskLruCacheFactory(diskCachePath, diskCacheSize));}
}import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.module.AppGlideModule;
import okhttp3.Cache;
import okhttp3.OkHttpClient;import java.io.InputStream;/*** 自定义Glide网络缓存配置* 结合OkHttp实现HTTP级别的网络缓存*/
public class CustomNetworkCacheGlideModule extends AppGlideModule {@Overridepublic void registerComponents(Context context, Glide glide, Registry registry) {// 创建OkHttp缓存(50MB,位于应用缓存目录)Cache cache = new Cache(context.getCacheDir(), 1024 * 1024 * 50);// 构建带缓存的OkHttpClientOkHttpClient client = new OkHttpClient.Builder().cache(cache).build();// 注册OkHttp为Glide的网络请求引擎registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client));}
}

常见问题解决方案

  1. 缓存穿透:缓存穿透指查询一个一定不存在的数据,由于缓存不命中需要从数据库查询,查不到数据则不写入缓存,导致该不存在的数据每次请求都要到数据库查询,给数据库带来压力。在 Glide 中,可以通过设置错误占位图、加载占位图和空值占位图来解决部分问题。例如:
Glide.with(context).load(url).error(R.drawable.ic_error) // 设置错误占位图.placeholder(R.drawable.ic_loading) // 设置加载占位图.fallback(R.drawable.ic_fallback) // 设置空值占位图.into(imageView);

在大厂面试中,关于缓存穿透常被问到的问题有:“请简述缓存穿透的概念以及可能的解决方案”。回答时,除了像上述代码那样通过 Glide 的占位图设置来应对外,还可以提及如使用布隆过滤器(Bloom Filter)等方案。布隆过滤器是一种空间效率极高的概率型数据结构,它利用位数组和哈希函数来判断一个元素是否在一个集合中。将所有已存在的数据 key 放入布隆过滤器中,当新的请求到来时,先通过布隆过滤器判断该 key 是否存在。如果不存在,直接返回,避免查询数据库,从而有效减少不必要的数据库查询,提高系统性能。

  1. 缓存雪崩:缓存雪崩是指在某一时刻,大量缓存同时失效,导致大量请求直接访问数据库,造成数据库压力过大甚至崩溃。可以通过设置不同的缓存过期时间来避免,例如:
int cacheDuration = TimeUnit.HOURS.toMillis(24) + new Random().nextInt(3600000);

面试中可能会被问到:“如何防止缓存雪崩的发生”。除了上述设置随机过期时间的方法外,还可以采用二级缓存策略,即设置主缓存和备用缓存。主缓存失效后,先从备用缓存获取数据,同时对主缓存进行异步更新,这样可以在一定程度上缓解大量请求直接冲击数据库的问题。另外,使用互斥锁也是一种思路,在缓存失效时,只有一个线程能够获取锁去更新缓存,其他线程等待,避免大量线程同时查询数据库。

  1. OOM 预防:OOM(Out Of Memory,内存溢出)在图片加载中较为常见,因为图片占用内存较大。可以通过使用 RGB_565 格式减少内存占用,例如:
// 使用RGB_565格式减少内存占用
Glide.with(context).load(url).format(DecodeFormat.PREFER_RGB_565).into(imageView);

面试官可能会问:“在 Glide 中,如何预防 OOM 问题”。除了设置图片格式外,还可以根据设备内存情况动态调整图片尺寸。例如,获取设备的可用内存,当内存较低时,对图片进行更大比例的压缩。同时,合理配置 Glide 的内存缓存大小也很关键,避免缓存占用过多内存。此外,及时释放不再使用的图片资源,Glide 通过与 Activity 或 Fragment 的生命周期绑定,在界面不可见时及时清理相关图片资源,防止内存泄漏。

关键指标的获取途径

  1. 冷启动加载时间:借助 Android Profiler 的 Timeline 功能来精准测量。在应用启动时,启动 Profiler 并记录图片加载所耗费的时长。代码示例如下:
long startTime = System.currentTimeMillis();
Glide.with(this).load(url).into(imageView);
long duration = System.currentTimeMillis() - startTime;
Log.d("GlideTest", "加载耗时: " + duration + "ms");
  1. 内存峰值占用情况:使用 Android Profiler 的 Memory Monitor 进行监测。在滑动列表时,留意 Heap Size 的变化趋势,对比开启缓存前后 Bitmap 内存占用的差异,以此来优化内存使用。
  2. 缓存命中率计算:通过 Glide 的日志输出(设置 Glide.get (context).setLogLevel (Log.DEBUG)),从日志中筛选出 Fetched 和 Decoded 相关的条目。缓存命中率 = (内存命中数 + 磁盘命中数)÷ 总请求数 × 100%。
  3. FPS 帧率监控:采用 Android Profiler 的 FrameMetrics 功能。在滑动列表的过程中,记录丢帧的数量,确保平均帧率稳定在 55fps 以上,以保证流畅的用户体验。

二、自定义图片缓存框架

设计思路

  1. 内存缓存:运用 LruCache(Least Recently Used Cache,最近最少使用缓存)实现内存缓存,它能够自动回收最近最少使用的图片,保障内存的合理使用。
  2. 磁盘缓存:利用 DiskLruCache 实现磁盘缓存,将图片持久化到本地磁盘,方便在网络不可用或需要重复使用图片时快速获取。
  3. 多级缓存策略:首先从内存缓存中查找图片,若未找到则从磁盘缓存中查找,最后才从网络请求图片。当从网络获取到图片后,同时将其存入内存缓存和磁盘缓存。

代码实现

import android.graphics.Bitmap;
import android.util.LruCache;/*** 内存缓存实现* 使用LruCache(最近最少使用)算法管理内存中的图片*/
public class MemoryCache {// LruCache实例,用于存储图片(键为图片URL,值为Bitmap)private LruCache<String, Bitmap> lruCache;public MemoryCache() {// 获取应用最大可用内存(KB)int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);// 设置缓存大小为最大内存的1/8int cacheSize = maxMemory / 8;// 初始化LruCache并重写sizeOf方法计算每个Bitmap的大小lruCache = new LruCache<String, Bitmap>(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {// 返回Bitmap占用的内存大小(KB)return bitmap.getByteCount() / 1024;}};}// 向缓存添加图片public void put(String key, Bitmap bitmap) {if (get(key) == null) {lruCache.put(key, bitmap);}}// 从缓存获取图片public Bitmap get(String key) {return lruCache.get(key);}// 从缓存移除图片public void remove(String key) {lruCache.remove(key);}
}import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import com.jakewharton.disklrucache.DiskLruCache;import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;/*** 磁盘缓存实现* 使用DiskLruCache将图片持久化到本地存储*/
public class DiskCache {// 应用版本(用于缓存版本控制)private static final int APP_VERSION = 1;// 每个缓存项对应的值数量private static final int VALUE_COUNT = 1;// 磁盘缓存最大容量(10MB)private static final long CACHE_SIZE = 10 * 1024 * 1024;// DiskLruCache实例private DiskLruCache diskLruCache;public DiskCache(Context context) {try {// 获取缓存目录File cacheDir = getDiskCacheDir(context, "bitmap");if (!cacheDir.exists()) {cacheDir.mkdirs();}// 打开DiskLruCache实例diskLruCache = DiskLruCache.open(cacheDir, APP_VERSION, VALUE_COUNT, CACHE_SIZE);} catch (IOException e) {e.printStackTrace();}}// 向磁盘缓存添加图片public void put(String key, Bitmap bitmap) {DiskLruCache.Editor editor = null;try {// 获取缓存编辑器editor = diskLruCache.edit(hashKeyForDisk(key));if (editor != null) {// 获取输出流并写入图片(JPEG格式,质量100%)OutputStream outputStream = editor.newOutputStream(0);if (bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)) {editor.commit();} else {editor.abort();}outputStream.close();}} catch (IOException e) {e.printStackTrace();}}// 从磁盘缓存获取图片public Bitmap get(String key) {try {// 获取缓存快照DiskLruCache.Snapshot snapshot = diskLruCache.get(hashKeyForDisk(key));if (snapshot != null) {// 从输入流解码BitmapInputStream inputStream = snapshot.getInputStream(0);return BitmapFactory.decodeStream(inputStream);}} catch (IOException e) {e.printStackTrace();}return null;}// 从磁盘缓存移除图片public void remove(String key) {try {diskLruCache.remove(hashKeyForDisk(key));} catch (IOException e) {e.printStackTrace();}}// 获取磁盘缓存目录private File getDiskCacheDir(Context context, String uniqueName) {String cachePath;// 判断外部存储是否可用if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())||!Environment.isExternalStorageRemovable()) {cachePath = context.getExternalCacheDir().getPath();} else {cachePath = context.getCacheDir().getPath();}return new File(cachePath + File.separator + uniqueName);}// 生成URL的MD5哈希值作为缓存键private String hashKeyForDisk(String key) {String cacheKey;try {// 使用MD5算法生成哈希值final MessageDigest mDigest = MessageDigest.getInstance("MD5");mDigest.update(key.getBytes());cacheKey = bytesToHexString(mDigest.digest());} catch (NoSuchAlgorithmException e) {// 若不支持MD5,使用普通哈希码cacheKey = String.valueOf(key.hashCode());}return cacheKey;}// 字节数组转十六进制字符串private String bytesToHexString(byte[] bytes) {StringBuilder sb = new StringBuilder();for (byte b : bytes) {String hex = Integer.toHexString(0xFF & b);if (hex.length() == 1) {sb.append('0');}sb.append(hex);}return sb.toString();}
}import android.content.Context;
import android.graphics.Bitmap;/*** 多级缓存管理器* 统一管理内存缓存和磁盘缓存*/
public class ImageCacheManager {// 内存缓存实例private MemoryCache memoryCache;// 磁盘缓存实例private DiskCache diskCache;public ImageCacheManager(Context context) {memoryCache = new MemoryCache();diskCache = new DiskCache(context);}// 同时存入内存缓存和磁盘缓存public void put(String key, Bitmap bitmap) {memoryCache.put(key, bitmap);diskCache.put(key, bitmap);}// 优先从内存缓存获取,再从磁盘缓存获取public Bitmap get(String key) {Bitmap bitmap = memoryCache.get(key);if (bitmap != null) {return bitmap;}bitmap = diskCache.get(key);if (bitmap != null) {// 从磁盘读取后存入内存,提升下次访问速度memoryCache.put(key, bitmap);}return bitmap;}
}
  1. 请简述三级缓存(内存缓存、磁盘缓存、网络缓存)的作用和原理。

    • 内存缓存:旨在快速显示刚浏览过的图片,使用 LruCache 算法(最近最少使用),自动清理长时间未使用的图片,确保内存的合理利用。通常限制在手机可用内存的 15%。
    • 磁盘缓存:用于存储常用但当前不在内存中的图片,通过 DiskLruCache 将图片存储在手机硬盘上,设置总容量(如 100MB),优先存储高质量图片,按 URL 哈希值命名文件以避免重复存储,超过 7 天未使用的图片会自动清理。
    • 网络缓存:避免重复从服务器下载相同图片,结合 HTTP 缓存头,借助 OkHttp 的缓存机制,将图片存储在路由器或基站缓存中,设置总容量(如 50MB),优先存储高频访问的图片,根据 HTTP 的 Cache-Control 头设置缓存时间,并在图片 URL 中添加版本号以强制重新下载。
  2. 在自定义图片缓存框架中,LruCache 和 DiskLruCache 分别是如何实现的?

    • LruCache:在内存缓存类中,获取应用程序运行时的最大可用内存,使用最大可用内存的一部分(如 1/8)作为 LruCache 的缓存大小。重写 sizeOf 方法,计算每个图片对象占用的内存大小,通过 put 方法添加图片到缓存,get 方法获取图片,remove 方法移除图片。
    • DiskLruCache:在磁盘缓存类中,初始化时获取磁盘缓存的目录,打开 DiskLruCache 实例。put 方法通过获取编辑器和输出流,将图片以 JPEG 格式压缩并写入;get 方法通过获取快照和输入流,将输入流解码为 Bitmap 对象;remove 方法移除指定的图片。对键进行 MD5 哈希处理,确保键的唯一性。
  3. 如何防止缓存穿透、缓存雪崩和 OOM 问题?

    • 缓存穿透:在 Glide 中,可以通过设置错误占位图、加载占位图和空值占位图来解决部分问题。另外,可以使用布隆过滤器,将所有已存在的数据 key 放入布隆过滤器中,当新的请求到来时,先通过布隆过滤器判断该 key 是否存在,避免不必要的数据库查询。
    • 缓存雪崩:可以通过设置不同的缓存过期时间来避免,例如在设置缓存过期时间时,添加一个随机值。另外,采用二级缓存策略,设置主缓存和备用缓存,主缓存失效后,先从备用缓存获取数据,同时对主缓存进行异步更新。使用互斥锁,在缓存失效时,只有一个线程能够获取锁去更新缓存,其他线程等待。
    • OOM:在 Glide 中,可以使用 RGB_565 格式减少内存占用,根据设备内存情况动态调整图片尺寸,合理配置 Glide 的内存缓存大小,避免缓存占用过多内存。及时释放不再使用的图片资源,Glide 通过与 Activity 或 Fragment 的生命周期绑定,在界面不可见时及时清理相关图片资源,防止内存泄漏。

相关文章:

Android学习总结之Glide自定义三级缓存(实战篇)

一、为什么需要三级缓存 内存缓存&#xff08;Memory Cache&#xff09; 内存缓存旨在快速显示刚浏览过的图片&#xff0c;例如在滑动列表时来回切换的图片。在 Glide 中&#xff0c;内存缓存使用 LruCache 算法&#xff08;最近最少使用&#xff09;&#xff0c;能自动清理长…...

单片机开发软件

目录 纯编码 vscode Ardunio Keil 1. 集成化开发环境&#xff08;IDE&#xff09; 2. 多架构芯片支持 3. 高效的代码生成与优化 4. 强大的调试与仿真功能 5. 丰富的库函数与生态系统 6. 教育与企业级适用性 典型应用场景 半编码半图形化 STM32CUBEIED 1. 图形化配置…...

LeetCode100.2 字母异位词分组

观察题目&#xff0c;需要把strs中的元素按照字母进行归类&#xff0c;一个朴素的思路是&#xff1a;遍历strs&#xff0c;对每个元素排序后插入哈希表中&#xff0c;随后再遍历一遍表将其转化为vector<vector<string>>。 class Solution { public:vector<vect…...

深入了解 Stable Diffusion:AI 图像生成的奥秘

一、引言 AI 艺术与图像生成技术的兴起改变了我们创造和体验视觉内容的方式。在过去几年里&#xff0c;深度学习模型已经能够创造出令人惊叹的艺术作品&#xff0c;这些作品不仅模仿了人类艺术家的风格&#xff0c;甚至还能创造出前所未有的新风格。在这个领域&#xff0c;Sta…...

Python爬虫实战:研究ajax异步渲染加密

一、引言 在当今数字化时代,数据已成为推动各行业发展的核心驱动力。网络爬虫作为一种高效的数据采集工具,能够从互联网上自动获取大量有价值的信息。然而,随着 Web 技术的不断发展,越来越多的网站采用了 AJAX(Asynchronous JavaScript and XML)异步渲染技术来提升用户体…...

大语言模型训练的两个阶段

先说结论&#xff1a;第一阶段在云平台训练至收敛 第二阶段本地GPU微调 一、阶段划分的核心逻辑 阶段目标资源特点典型耗时占比成本敏感度预训练获取通用表征能力需要大规模分布式计算70-90%高&#xff08;追求每美元算力&#xff09;微调适配特定任务需要领域数据安全/低延迟…...

显示的图标跟UI界面对应不上。

图片跟UI界面不符合。 要找到对应dp的值。UI的dp要跟代码里的xml文件里的dp要对应起来。 蓝湖里设置一个宽度给对应上。然后把对应的值填入xml. 一个屏幕上的图片到底是用topmarin来设置&#xff0c;还是用bottommarin来设置。 因为第一节&#xff0c;5&#xff0c;7 车厢的…...

OJ判题系统第6期之判题逻辑开发——设计思路、实现步骤、代码实现(策略模式)

在看这期之前&#xff0c;建议先看前五期&#xff1a; Java 原生实现代码沙箱&#xff08;OJ判题系统第1期&#xff09;——设计思路、实现步骤、代码实现-CSDN博客 Java 原生实现代码沙箱之Java 程序安全控制&#xff08;OJ判题系统第2期&#xff09;——设计思路、实现步骤…...

css中的 vertical-align与line-height作用详解

一、vertical-align 详解 作用对象&#xff1a;行内元素&#xff08;inline/inline-block&#xff09;或表格单元格内容核心功能&#xff1a;控制元素在行框内的垂直对齐方式常用取值&#xff1a; baseline&#xff08;默认&#xff09;&#xff1a;基线与父元素基线对齐top&a…...

vue数据可视化开发echarts等组件、插件的使用及建议-浅看一下就行

在 Vue 项目中使用 ECharts 进行数据可视化开发时&#xff0c;可以结合 Vue 的响应式特性和 ECharts 的强大功能&#xff0c;实现动态、交互式的图表展示。 一、ECharts 基础使用 1. 安装 ECharts npm install echarts2. 在 Vue 组件中使用 ECharts <template><div…...

高并发内存池(三):TLS无锁访问以及Central Cache结构设计

目录 前言&#xff1a; 一&#xff0c;thread cache线程局部存储的实现 问题引入 概念说明 基本使用 thread cache TLS的实现 二&#xff0c;Central Cache整体的结构框架 大致结构 span结构 span结构的实现 三&#xff0c;Central Cache大致结构的实现 单例模式 thr…...

在Taro中开发一个跨端Svg组件,同时支持小程序、H5、React Native

Taro系列中一直没有跨端的绘图工具&#xff0c;小程序端支持canvas但是不支持svg&#xff0c;RN端有 react-native-svg 支持svg&#xff0c;但是没有很好原生的canvas插件&#xff0c;社区的canvas都是基于WebView实现的&#xff0c;或者skia&#xff0c;这个插件的书写方式和c…...

【办公类-100-01】20250515手机导出教学照片,自动上传csdn+最大化、最小化Vs界面

背景说明&#xff1a; 每次把教学照片上传csdn&#xff0c;都需要打开相册&#xff0c;一张张截图&#xff0c;然后ctrlV黏贴到CSDN内&#xff0c;我觉得太烦了。 改进思路&#xff1a; 是否可以先把所有照片都上传到csdn&#xff0c;然后再一张张的截图&#xff08;去掉幼儿…...

SIP协议栈--osip源码梳理

文章目录 osiposip主体结构体code main函数 状态机转化结构体code状态转换 sip事务结构体code osip_dialog结构体code 创建并发送200 OK响应 osip_message结构体code osip_eventcode 打印接收到的SIP消息 osip OSIP&#xff08;Open Source Implementation of SIP&#xff09;…...

Python零基础入门到高手8.4节: 元组与列表的区别

目录 8.4.1 不可变数据类型 8.4.2 可变数据类型 8.4.3 元组与列表的区别 8.4.4 今天彩票没中奖 8.4.1 不可变数据类型 不可变数据类型是指不可以对该数据类型进行原地修改&#xff0c;即只读的数据类型。迄今为止学过的不可变数据类型有字符串&#xff0c;元组。 在使用[]…...

深度学习入门:深度学习(完结)

目录 1、加深网络1.1 向更深的网络出发1.2 进一步提高识别精度1.3 加深层的动机 2、深度学习的小历史2.1 ImageNet2.2 VGG2.3 GoogleNet2.4 ResNet 3、深度学习的高速化3.1 需要努力解决的问题3.2 基于GPU的高速化3.3 分布式学习3.4 运算精度的位数缩减 4、深度学习的应用案例4…...

OpenCV CUDA模块中矩阵操作------矩阵元素求和

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在OpenCV的CUDA模块中&#xff0c;矩阵元素求和类函数主要用于计算矩阵元素的总和、绝对值之和以及平方和。这些操作对于图像处理中的特征提取、…...

使用Scrapeless Scraping Browser的自动化和网页抓取最佳实践

引言&#xff1a;人工智能时代浏览器自动化和数据收集的新范式 随着生成性人工智能、人工智能代理和数据密集型应用程序的快速崛起&#xff0c;浏览器正在从传统的“用户互动工具”演变为智能系统的“数据执行引擎”。在这一新范式中&#xff0c;许多任务不再依赖单一的API端点…...

java数组题(5)

&#xff08;1&#xff09;&#xff1a; 思路&#xff1a; 1.首先要对数组nums排序&#xff0c;这样两数之间的差距最小。 2.题目要求我们通过最多 k 次递增操作&#xff0c;使数组中某个元素的频数&#xff08;出现次数&#xff09;最大化。经过上面的排序&#xff0c;最大数…...

使用Thrust库实现异步操作与回调函数

文章目录 使用Thrust库实现异步操作与回调函数基本异步操作插入回调函数更复杂的回调示例注意事项 使用Thrust库实现异步操作与回调函数 在Thrust库中&#xff0c;你可以通过CUDA流(stream)来实现异步操作&#xff0c;并在适当的位置插入回调函数。以下是如何实现的详细说明&a…...

物联网无线传感方向专业词汇解释

涡旋电磁波(VEMW)&#xff1a;一种具有轨道角动量的电磁波&#xff0c;其特性在于能够在传播过程中携带额外的相位信息&#xff0c;从而增加通信系统的容量和灵活性。波前&#xff1a;波动传播过程中&#xff0c;同一时刻振动相位相同的所有点构成的几何曲面&#xff0c;代表波…...

Maven 插件参数注入与Mojo开发详解

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…...

C++中void*知识详解和注意事项

一、void* 是什么&#xff1f; 在 C/C 中&#xff0c;void* 表示一个通用指针类型&#xff08;generic pointer&#xff09;&#xff0c;可以指向任意类型的对象&#xff0c;但 不能直接解引用或进行算术运算&#xff0c;必须先进行类型转换。 void* ptr; // 可以指向任意类型…...

2024年全国青少年信息素养大赛——算法创意实践挑战赛复赛真题(小学组)——玫瑰花地的面积

2024年全国青少年信息素养大赛——算法创意实践挑战赛复赛真题(小学组)——玫瑰花地的面积 上面试卷可点下方&#xff0c;支持在线编程&#xff0c;在线测评&#xff5e; 2024年全国信息素养大赛 算法创意实践挑战赛复赛(小学组)_c_少儿编程题库学习中心-嗨信奥 5月17号 全国青…...

【补充笔记】修复“NameError: name ‘ZhNormalizer‘ is not defined”的直接方法

#工作记录 一、问题描述 在运行CosyVoice_For_Windows项目时&#xff0c;出现以下报错&#xff1a; File "F:\PythonProjects\CosyVoice_For_Windows\cosyvoice\cli\frontend.py", line 74, in __init__ self.zh_tn_model ZhNormalizer(remove_erhuaFalse, fu…...

预训练模型实战手册:用BERT/GPT-2微调实现10倍效率提升,Hugging Face生态下的迁移学习全链路实践

更多AI大模型应用开发学习内容&#xff0c;尽在聚客AI学院。 一. 预训练模型&#xff08;PTM&#xff09;核心概念 1.1 什么是预训练模型&#xff1f; 预训练模型&#xff08;Pre-trained Model, PTM&#xff09;是在大规模通用数据上预先训练的模型&#xff0c;通过自监督学…...

并发笔记-给数据上锁(二)

文章目录 核心挑战 (The CRUX)29.1 并发计数器 (Concurrent Counters)1. 简单非并发计数器 (Figure 29.1)2. 同步计数器&#xff08;单锁版本 - Coarse-Grained Lock, Figure 29.2&#xff09;3. 可伸缩计数&#xff1a;近似/懒惰计数器 (Approximate/Sloppy Counter, Figure 2…...

mac docker弹窗提示Docker 启动没有响应

一、原因分析 这台笔记电脑是Mac M3操作系统,安装Docker之后,Docker应用程序一直启动不起来。 二、解决办法 sudo rm /Library/PrivilegedHelperTools/com.docker.vmnetd sudo cp /Applications/Docker.app/Contents/Library/LaunchServices/com.docker.vmnetd /Library/Pri…...

每日算法刷题计划Day7 5.15:leetcode滑动窗口4道题,用时1h

一.定长滑动窗口 【套路】教你解决定长滑窗&#xff01;适用于所有定长滑窗题目&#xff01; 模版套路 1.题目描述 1.计算所有长度恰好为 k 的子串中&#xff0c;最多可以包含多少个元音字母 2.找出平均数最大且 长度为 k 的连续子数组&#xff0c;并输出该最大平均数。 3.…...

如何利用 Python 爬虫按关键字搜索京东商品:实战指南

在电商领域&#xff0c;京东作为国内知名的电商平台&#xff0c;拥有海量的商品数据。通过 Python 爬虫技术&#xff0c;我们可以高效地按关键字搜索京东商品&#xff0c;并获取其详细信息。这些信息对于市场分析、选品上架、库存管理和价格策略制定等方面具有重要价值。本文将…...