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));}
}
常见问题解决方案
- 缓存穿透:缓存穿透指查询一个一定不存在的数据,由于缓存不命中需要从数据库查询,查不到数据则不写入缓存,导致该不存在的数据每次请求都要到数据库查询,给数据库带来压力。在 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 是否存在。如果不存在,直接返回,避免查询数据库,从而有效减少不必要的数据库查询,提高系统性能。
- 缓存雪崩:缓存雪崩是指在某一时刻,大量缓存同时失效,导致大量请求直接访问数据库,造成数据库压力过大甚至崩溃。可以通过设置不同的缓存过期时间来避免,例如:
int cacheDuration = TimeUnit.HOURS.toMillis(24) + new Random().nextInt(3600000);
面试中可能会被问到:“如何防止缓存雪崩的发生”。除了上述设置随机过期时间的方法外,还可以采用二级缓存策略,即设置主缓存和备用缓存。主缓存失效后,先从备用缓存获取数据,同时对主缓存进行异步更新,这样可以在一定程度上缓解大量请求直接冲击数据库的问题。另外,使用互斥锁也是一种思路,在缓存失效时,只有一个线程能够获取锁去更新缓存,其他线程等待,避免大量线程同时查询数据库。
- 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 的生命周期绑定,在界面不可见时及时清理相关图片资源,防止内存泄漏。
关键指标的获取途径
- 冷启动加载时间:借助 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");
- 内存峰值占用情况:使用 Android Profiler 的 Memory Monitor 进行监测。在滑动列表时,留意 Heap Size 的变化趋势,对比开启缓存前后 Bitmap 内存占用的差异,以此来优化内存使用。
- 缓存命中率计算:通过 Glide 的日志输出(设置 Glide.get (context).setLogLevel (Log.DEBUG)),从日志中筛选出 Fetched 和 Decoded 相关的条目。缓存命中率 = (内存命中数 + 磁盘命中数)÷ 总请求数 × 100%。
- FPS 帧率监控:采用 Android Profiler 的 FrameMetrics 功能。在滑动列表的过程中,记录丢帧的数量,确保平均帧率稳定在 55fps 以上,以保证流畅的用户体验。
二、自定义图片缓存框架
设计思路
- 内存缓存:运用 LruCache(Least Recently Used Cache,最近最少使用缓存)实现内存缓存,它能够自动回收最近最少使用的图片,保障内存的合理使用。
- 磁盘缓存:利用 DiskLruCache 实现磁盘缓存,将图片持久化到本地磁盘,方便在网络不可用或需要重复使用图片时快速获取。
- 多级缓存策略:首先从内存缓存中查找图片,若未找到则从磁盘缓存中查找,最后才从网络请求图片。当从网络获取到图片后,同时将其存入内存缓存和磁盘缓存。
代码实现
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;}
}
-
请简述三级缓存(内存缓存、磁盘缓存、网络缓存)的作用和原理。
- 内存缓存:旨在快速显示刚浏览过的图片,使用 LruCache 算法(最近最少使用),自动清理长时间未使用的图片,确保内存的合理利用。通常限制在手机可用内存的 15%。
- 磁盘缓存:用于存储常用但当前不在内存中的图片,通过 DiskLruCache 将图片存储在手机硬盘上,设置总容量(如 100MB),优先存储高质量图片,按 URL 哈希值命名文件以避免重复存储,超过 7 天未使用的图片会自动清理。
- 网络缓存:避免重复从服务器下载相同图片,结合 HTTP 缓存头,借助 OkHttp 的缓存机制,将图片存储在路由器或基站缓存中,设置总容量(如 50MB),优先存储高频访问的图片,根据 HTTP 的 Cache-Control 头设置缓存时间,并在图片 URL 中添加版本号以强制重新下载。
-
在自定义图片缓存框架中,LruCache 和 DiskLruCache 分别是如何实现的?
- LruCache:在内存缓存类中,获取应用程序运行时的最大可用内存,使用最大可用内存的一部分(如 1/8)作为 LruCache 的缓存大小。重写 sizeOf 方法,计算每个图片对象占用的内存大小,通过 put 方法添加图片到缓存,get 方法获取图片,remove 方法移除图片。
- DiskLruCache:在磁盘缓存类中,初始化时获取磁盘缓存的目录,打开 DiskLruCache 实例。put 方法通过获取编辑器和输出流,将图片以 JPEG 格式压缩并写入;get 方法通过获取快照和输入流,将输入流解码为 Bitmap 对象;remove 方法移除指定的图片。对键进行 MD5 哈希处理,确保键的唯一性。
-
如何防止缓存穿透、缓存雪崩和 OOM 问题?
- 缓存穿透:在 Glide 中,可以通过设置错误占位图、加载占位图和空值占位图来解决部分问题。另外,可以使用布隆过滤器,将所有已存在的数据 key 放入布隆过滤器中,当新的请求到来时,先通过布隆过滤器判断该 key 是否存在,避免不必要的数据库查询。
- 缓存雪崩:可以通过设置不同的缓存过期时间来避免,例如在设置缓存过期时间时,添加一个随机值。另外,采用二级缓存策略,设置主缓存和备用缓存,主缓存失效后,先从备用缓存获取数据,同时对主缓存进行异步更新。使用互斥锁,在缓存失效时,只有一个线程能够获取锁去更新缓存,其他线程等待。
- OOM:在 Glide 中,可以使用 RGB_565 格式减少内存占用,根据设备内存情况动态调整图片尺寸,合理配置 Glide 的内存缓存大小,避免缓存占用过多内存。及时释放不再使用的图片资源,Glide 通过与 Activity 或 Fragment 的生命周期绑定,在界面不可见时及时清理相关图片资源,防止内存泄漏。
相关文章:
Android学习总结之Glide自定义三级缓存(实战篇)
一、为什么需要三级缓存 内存缓存(Memory Cache) 内存缓存旨在快速显示刚浏览过的图片,例如在滑动列表时来回切换的图片。在 Glide 中,内存缓存使用 LruCache 算法(最近最少使用),能自动清理长…...
Maven 下载安装与配置教程
## 1. Maven 简介 Maven 是一个项目管理和构建自动化工具,主要用于 Java 项目。Maven 可以帮助开发者管理项目的构建、报告和文档,简化项目依赖管理。 ## 2. 下载 Maven 1. 访问 Maven 官方网站 [https://maven.apache.org/download.cgi](https://maven.…...
一篇解决Redis:持久化机制
目录 认识持久化 持久化方案 RDB(Redis DataBase) 手动触发 自动触发 小结 AOF(Append-Only File) AOF缓冲区刷新机制 AOF重写机制 AOF重写流程 编辑 混合持久化 认识持久化 我们都知道Mysql有四大特征,原子性,持久…...
使用IDEA创建Maven版本的web项目以及lombok的使用
1.新建项目 2.修改pom.xml 3.修改项目结构 4.在main/java下面写一个Servlet测试一下 然后当前页面往下滑 -Dfile.encodingUTF-8编写一句输出语句,测试是否成功部署配置,并选择到正确的位置: 回车以后 再回到idea里面,发现控…...
2025年AI开发者在开发者占比?
AI开发者在全球开发者中的占比目前没有一个统一且精确的数值,但根据行业报告和调研数据,可以给出以下大致的范围和趋势分析: 1. 综合估算范围 全球范围:AI/ML(机器学习)开发者约占开发者总数的 5%-15%&…...
SpringBoot整合MQTT实战:基于EMQX构建高可靠物联网通信,从零到一实现设备云端双向对话
一、引言 随着物联网(IoT)技术的快速发展,MQTT(Message Queuing Telemetry Transport)协议因其轻量级、低功耗和高效的特点,已成为物联网设备通信的事实标准。本文将详细介绍如何使用SpringBoot框架整合MQTT协议,基于开源MQTT代理EMQX实现设…...
Windows更新暂停七天关键注册表
环境:windows10 工具:procmon 下载地址:https://learn.microsoft.com/zh-cn/sysinternals/downloads/procmon 监控截图: 界面截图: 注: 1.北京时间差8小时 2.至少是从第二天恢复,即至少暂停…...
小白学习java第18天(上):spring
Spring :是一个轻量级(一个小依赖就可以实现还不是轻量级)的控制反转(IOC)和面向切面编程(AOP)的框架! 优点: 1.Spring 是一个开源免费的框架(容器…...
用Array.from实现创建一个1-100的数组
一、代码实现 let arr Array.from({length: 100}, (_, i) > i 1); 二、代码分析 1、Array.from(arrayLike, mapFn) (1)arrayLike 类数组对象(如 { length: 100 })本身没有索引属性(如 0: undefined, 1: undefi…...
什么是物联网 IoT 平台?
目录 物联网IoT平台的定义 物联网 IoT 平台发展历程 物联网IoT平台数据的特征 物联网IoT平台的处理流程 专为物联网 IoT 平台处理而生的时序数据库 物联网 IoT 平台时序数据处理面临的挑战及解决方案 收益与价值 物联网 IoT 平台企业案例 至数摇光 x TDengine 华自科技…...
PostgREST:无需后端 快速构建RESTful API服务
在现代 Web 开发中,API 已成为连接前后端的核心桥梁,传统的做法是通过后端框架来构建API接口,然后由前后端人员进行联调。 PostgREST是基于无服务器的一种实现方案,允许开发者将PostgreSQL数据库直接暴露为RESTful API࿰…...
3天北京旅游规划
北京 第一天应该集中在故宫和市中心区域,比如天安门、人民广场。这样可以体验到北京的历史和政治文化。午餐推荐烤鸭,因为这可是北京的特色。下午可以安排南锣鼓巷,既有古色古香的胡同,又有丰富的美食选择。 第二天的话࿰…...
MySQL--day1--数据库概述
(以下内容全部来自上述课程) 概述 1. 为什么要用数据库 持久化:内存中的数据断电之后就不存在了,所以需要持久化–>需要相关介质。 其中的一个介质就是数据库:存储数据量大、存储数据类型多 2. 数据库与数据库…...
[思维模式-38]:看透事物的关系:什么是事物的关系?事物之间的关系的种类?什么是因果关系?如何通过数学的方式表达因果关系?
一、什么是事物的关系? 事物的关系是指不同事物之间存在的各种联系和相互作用,它反映了事物之间的相互依存、相互影响、相互制约等特性。以下从不同维度为你详细阐述: 1、关系的类型 因果关系 定义:一个事件(原因&a…...
图像识别与 OCR 应用实践
图像识别是一种让计算机具备“看”与“理解”图像能力的人工智能技术,其目标是从图像或视频中提取有意义的信息,如物体、人物、场景或文字。在现实生活中,这项技术被广泛应用于面部识别、自动驾驶、安防监控、医疗诊断、图像搜索等多个领域。…...
深入理解卷积神经网络:从基础原理到实战应用
在人工智能领域,卷积神经网络(Convolutional Neural Network,简称 CNN)凭借其强大的图像识别、处理能力,成为深度学习中不可或缺的技术。无论是自动驾驶汽车识别道路标志,还是医学影像分析辅助疾病诊断&…...
51单片机——交通指示灯控制器设计
设计目标 1、设计一交通灯控制,控制东西方向的红、黄、绿灯和南北方向的红、黄、绿灯。 2、可手动控制和自动控制,设置两个输入控制开关。 手动/自动开关,通过P11的按键输入控制 3、手动:设置开关P11,两种情况&#x…...
vue2 头像上传+裁剪组件封装
背景:最近在进行公司业务开发时,遇到了头像上传限制尺寸的需求,即限制为一寸证件照(宽295像素,高413像素)。 用到的第三方库: "vue-cropper": "^0.5.5" 完整组件代码&…...
面向对象设计模式之代理模式详解
文章目录 面向对象设计模式之代理模式详解面向对象思想:现代软件开发的基石代理模式:巧妙的中间层设计JavaScript 语法点与代理模式的结合JavaScript 实现代理模式示例代理模式的应用场景 面向对象设计模式之代理模式详解 在现代软件开发的浩瀚领域中&a…...
Leetcode209做题笔记
力扣209 题目分析:想象一个窗口遍历着这个数组,不断扩大右边界,让r。往窗口中添加数字: 此时我们找到了这个窗口,它的和满足了大于等于target的条件,题目让我求最短的,那么我们就尝试来缩短它&…...
SVG 知识详解:从入门到精通
SVG 知识详解:从入门到精通 作为一名前端开发者,我经常会被SVG的魅力所折服。这种基于XML的矢量图形格式,不仅能完美适配各种屏幕分辨率,还能通过CSS和JavaScript进行灵活控制。今天,就让我们一起来深入探索SVG的世界…...
编译openssl源码
openssl版本 1.1.1c windows 安装环境 perl 先安装perl,生成makefile需要 https://strawberryperl.com/releases.html nasm nasm 也是生成makefile需要 https://www.nasm.us/ 安装完perl输入一下nasm,看看能不能找到,找不到的话需要配…...
土壤温湿盐分传感器用于节水农业灌溉引领者三针设计原理便于安装维护
土壤温度部分是由精密铂电阻和高精度变送器两部分组成。变送器部分由电源模块、温度传感模块、变送模块、温度补偿模块及数据处理模块等组成,彻底解决铂电阻因自身特点导入的测量误差,变送器内有零漂电路和温度补偿电路,对使用环境有较高的适…...
Kotlin Compose 与传统 Android UI 开发对比
在移动应用开发领域,Android 开发一直是技术演进的前沿阵地,而 UI 开发作为用户与应用交互的核心环节,其技术体系的变革更是备受瞩目。 技术演进背景 Android UI 开发体系发展脉络 原生 View 体系阶段 在早期的 Android 开发中,原生 View 体系占据了主导地位。开发者通…...
docker-compose——安装redis
文章目录 一、编写docker-compose.yaml文件二、编写redis.conf文件三、启动docker-compose 一、编写docker-compose.yaml文件 version: 3.3 services:redis:image: redis:latestcontainer_name: redisrestart: alwaysports:- 6379:6379volumes:- ./redis/data:/data- ./redis/…...
MFC 调用海康相机进行软触发
初始化相机类文件 #pragma once #include "MvCameraControl.h" class CMvCamera { public:CMvCamera();~CMvCamera();//初始化相机int InitCamera();int SaveCurrentImage(CString filePath);//关闭相机void CloseCamera();//设置int SetEnumValue(IN const char* s…...
第二章 变量和运算符
主要内容 关键字和标识符变量和常量八大基本数据类型Scanner键盘输入基本数据类型的类型转换算术运算符赋值运算符扩展赋值运算符比较运算符逻辑运算符三目运算符运算符的优先级别 学习目标 知识点要求关键字和标识符理解变量和常量掌握八大基本数据类型掌握Scanner键盘输入…...
【starrocks】StarRocks 常见 HTTP 操作与导入错误排查指南
文章目录 一、Stream Load:通过 HTTP 导入数据二、导入状态查询三、取消导入任务四、节点状态监控查看所有 Backend 状态:查看所有 Frontend 状态: 五、导入失败的排查方式1. 查询导入任务状态2. 下载详细错误日志3. 查看 FE/BE 节点日志FE 日…...
网络协议分析 实验七 FTP、HTTP、DHCP
文章目录 实验7.1 FTP协议练习二 使用浏览器登入FTP练习三 在窗口模式下,上传/下传数据文件实验7.2 HTTP(Hyper Text Transfer Protocol)练习二 页面提交练习三 访问比较复杂的主页实验7.3 DHCP(Dynamic Host Configuration Protocol) 实验7.1 FTP协议 dir LIST&…...
ET ProcessInnerSender类(实体) 分析
ProcessInnerSender 作用是进程内部发送Actor消息 字段 TIMEOUT_TIME 超时时间RpcId 用来累加requestCallback 存储RPC的回调事件list 用来获取MessageQueue中的Actor消息 方法 Awake 初始化在MessageQueue中注册待处理的消息队列Destroy 移除在MessageQueue中的消息队列U…...
远程连接工具
绿色轻便ToDesk https://www.todesk.com/download.html 向日葵 https://sunlogin.oray.com/download...
MySQL库级管理:数据库管理与存储引擎剖析
引言 各位数据库爱好者们好!今天我们要深入探讨MySQL数据库的基本操作,这是每位开发者必须掌握的"内功心法" 💪。无论你是刚接触MySQL的小白,还是需要复习基础的老手,这篇教程都将带你系统学习数据库的核心…...
MongoDB 的核心概念(文档、集合、数据库、BSON)是什么?
MongoDB 是一个面向文档的数据库,它的核心概念与传统的关系型数据库(RDBMS)有所不同。以下是它的四个主要核心概念: 文档 (Document) 定义: 文档是 MongoDB 中的基本数据单元。它类似于关系型数据库中的一行记录&#…...
占位符读取标准输入缓冲区规则
1、如果标准输入缓冲区中的前若干个字符都是空白字符,%s,%d,%f都能直接跳过并且从第一个非空白字符开始读取,但%c不能,而是直接读取。 2、%s遇到空白字符时停止,不会读取遇到的空白字符。 3、%d遇到非数字…...
使用Docker部署MongoDB
使用Docker部署MongoDB 使用Docker部署MongoDB是一种快速、便捷的方式,以下是详细步骤: 1. 基本部署 拉取MongoDB镜像 docker pull mongo:latest运行MongoDB容器(最简单方式) docker run --name mongodb -d -p 27017:27017 m…...
如何通过命令提示符(CMD)检查虚拟化是否已启用
想确认 Windows 11 或 10 电脑或笔记本上的虚拟化是否已启用,可以使用命令提示符或 PowerShell 中的 systeminfo 命令。之前已经介绍过使用图形界面的检查方法。在大多数现代系统中,虚拟化默认在 BIOS 中启用,但我们无需打开 BIOS/UEFI 固件菜单即可确认这一点。 虚拟化是一…...
【氮化镓】AlGaN合金中成分相关的辐射响应
2025年,美国宾夕法尼亚州立大学Miaomiao Jin等人基于分子动力学模拟方法,研究了AlGaN合金中成分依赖的辐射响应。研究结果表明,AlGaN合金的辐射损伤特性与铝含量密切相关:随铝含量增加,单次碰撞事件产生的缺陷减少,但由于累积效应,富铝系统更易形成扩展间隙缺陷簇,导致…...
『 测试 』软件测试全流程与Bug管理核心要点解析
文章目录 1 软件测试生命周期2 Bug2.1 Bug 的概念2.2 提出清晰的 Bug2.3 Bug 级别2.4 Bug 的生命周期2.5 作为测试人员与开发人员发生冲突怎么办 1 软件测试生命周期 软件测试贯穿于软件的整个生命周期; 需求分析 测试前需要对需求进行分析, 分析通常站在三个角度去考虑, 即用…...
【springcloud学习(dalston.sr1)】使用Feign实现接口调用(八)
该系列项目整体介绍及源代码请参照前面写的一篇文章【springcloud学习(dalston.sr1)】项目整体介绍(含源代码)(一) (一)Feign的理解 前面文章【springcloud学习(dalston.sr1)】服务消费者通过restTemplat…...
嵌入式Linux Qt开发:2、Qt creator简单配置、Qt Designer使用以及信号槽机制使用
一、前言 Qt creator比较轻量,而且是Qt自带的IDE,基本可以开箱即用,个人使用起来感觉还是比较舒服的,并且其自带的FakeVim也可以做一些Vim的配置,其主界面可以简单配置显示很多的窗口,让开发更加顺畅。 Q…...
【Vue 3 + Vue Router 4】如何正确重置路由实例(resetRouter)——避免“VueRouter is not defined”错误
在使用 Vue 3 和 Vue Router 4 开发中大型 SPA 应用时,我们经常会遇到需要动态添加或删除路由的场景。尤其是在权限控制和用户登出后重置路由的需求中,正确地实现 resetRouter 非常关键。 然而,许多开发者在迁移或初始化项目时,会…...
企业内部通讯,企业级即时通讯软件选择
企业内部的沟通往往涉及大量敏感信息,如商业机密、财务数据、客户信息等。BeeWorks IM即时通讯平台采用全私有化部署,企业可以将服务器部署在自己的数据中心或私有云环境中,确保所有数据的存储和传输都在企业可控的网络内完成。这种部署方式从…...
300. 最长递增子序列
理解最长递增子序列(LIS)是解决该问题的关键。子序列是从给定数组中按顺序选取的元素序列,例如数组 [1, 2, 3, 4, 5] 的子序列可以是 [2, 3, 4]。需要注意的是,子序列的元素在原数组中不一定是连续的。因此,最长递增子…...
MongoDB入门
1.MongoDB 基本概念详解 2.MongoDB 快速实战 3.MongoDB 核心操作与原理详解 Mongo 是 humongous 的中间部分,在英文里是“巨大无比”的意思。所以 MongoDB 可以翻译 成“巨大无比的数据库”,更优雅的叫法是“海量数据库”。Mongodb是一款非关系型数据库…...
MySQL基础原理
目录 一、MySQL架构 1、四层架构 2、MySQL运行机制 二、MySQL存储引擎 1、不同存储引擎对比 2、InnoDB存储结构 2.1 内存结构 2.2 磁盘结构 3、日志先行策略 3.1 核心思想 3.2 关键组件与流程 3.3 数据安全保证 3.3.1 崩溃恢复 3.3.2 持久性保障 一、MySQL架构 …...
Python刷题练习
文章目录 1.寻找相同字串2.密钥格式化3.五键键盘的输出4.单词重量5.输出指定字母在字符串的中的索引6.污染水域7.九宫格按键输入8.任务最优调度9.高效的任务规划 1.寻找相同字串 题目描述: 给你两个字符串t和p,要求从t中找到一个和p相同的连续子串,并输…...
基于GPUGEEK 平台进行深度学习
一、平台简介 GPUGEEK 是一个专注于提供 GPU 算力租赁服务的平台,在人工智能与深度学习领域为用户搭建起便捷的算力桥梁。它整合了丰富多样的 GPU 资源,涵盖 RTX - 4090、RTX - 3090、A100 - PCIE 等多种型号,满足不同用户在模型训练、数据处…...
基于Matlab的非线性Newmark法用于计算结构动力响应
基于Matlab的非线性Newmark法用于计算结构动力响应,主要针对材料非线性或几何非线性问题。 1. Newmark法基本原理 Newmark法是一种隐式时间积分方法,通过预估-校正步骤更新位移、速度和加速度: 预测步: un1unΔtvn…...
Oracle — PL-SQL
介绍 Oracle PL/SQL是专为Oracle数据库设计的过程化编程语言,深度融合SQL语句与结构化编程逻辑,旨在高效处理复杂数据操作与业务规则。其核心特征为“块结构”,程序由声明、执行、异常处理三部分组成,支持模块化开发,显…...
第七节第二部分:接口的综合案例
案例分析 代码: Student类 package com.Interface_Demo;public class Student {private String name;private char sex;private double score;public Student() {}public Student(String name, char sex, double score) {this.name name;this.sex sex;this.scor…...