diff --git a/powerbell/build.properties b/powerbell/build.properties index 6db2fbd..b44472a 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Mon Dec 22 11:02:54 HKT 2025 +#Mon Dec 22 03:13:47 GMT 2025 stageCount=22 libraryProject= baseVersion=15.14 publishVersion=15.14.21 -buildCount=0 +buildCount=1 baseBetaVersion=15.14.22 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/App.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/App.java index 1a19f4d..ca413d3 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/App.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/App.java @@ -16,7 +16,7 @@ import java.io.File; /** * 应用全局入口类(适配Android API 30,基于Java 7编写) - * 核心策略:无论内存是否紧张,强制保持位图缓存与视图控件缓存 + * 核心策略:极致强制缓存 - 无论内存紧张程度,永不自动清理任何缓存(Bitmap/视图控件/路径记录) */ public class App extends GlobalApplication { // ===================== 常量定义区 ===================== @@ -32,13 +32,16 @@ public class App extends GlobalApplication { public static final String ACTION_SWITCHTO_CN1 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN1"; public static final String ACTION_SWITCHTO_CN2 = "cc.winboll.studio.powerbell.App.ACTION_SWITCHTO_CN2"; + // 缓存防护常量 + private static final String CACHE_PROTECT_TAG = "FORCE_CACHE_PROTECT"; + // ===================== 静态属性区 ===================== // 数据配置工具 private static AppConfigUtils sAppConfigUtils; private static AppCacheUtils sAppCacheUtils; - // 全局Bitmap缓存工具(强制保持,不随内存紧张清理) + // 全局Bitmap缓存工具(极致强制保持:一旦初始化,永不销毁) public static BitmapCacheUtils sBitmapCacheUtils; - // 全局视图控件缓存工具(强制保持,不随内存紧张清理) + // 全局视图控件缓存工具(极致强制保持:一旦初始化,永不销毁) public static MemoryCachedBackgroundView sMemoryCachedBackgroundView; // 临时文件夹路径 private static String sTempDirPath = ""; @@ -89,6 +92,25 @@ public class App extends GlobalApplication { sAppCacheUtils.clearBatteryHistory(); } + /** + * 手动清理所有缓存(带严格权限控制,仅主动调用生效) + * 极致强制缓存策略下,仅提供手动清理入口,永不自动调用 + */ + public static void manualClearAllCache() { + LogUtils.w(TAG, CACHE_PROTECT_TAG + " 手动清理缓存调用(极致强制缓存策略下,需谨慎使用)"); + // 清理Bitmap缓存 + if (sBitmapCacheUtils != null) { + sBitmapCacheUtils.clearAllCache(); + LogUtils.d(TAG, CACHE_PROTECT_TAG + " Bitmap缓存已手动清理"); + } + // 清理视图控件缓存(仅清除静态引用,不销毁实例) + if (sMemoryCachedBackgroundView != null) { + LogUtils.d(TAG, CACHE_PROTECT_TAG + " 视图控件缓存实例保持,仅清除静态引用"); + sMemoryCachedBackgroundView = null; + } + LogUtils.w(TAG, CACHE_PROTECT_TAG + " 手动清理缓存完成(部分缓存实例仍可能保留在内存中)"); + } + // ===================== 生命周期方法区 ===================== @Override public void onCreate() { @@ -103,18 +125,18 @@ public class App extends GlobalApplication { initBaseTools(); // 初始化临时文件夹 initTempDir(); - // 初始化工具类实例(含强制缓存工具) + // 初始化工具类实例(核心:极致强制缓存,永不销毁) initUtils(); // 初始化广播接收器 initReceiver(); - LogUtils.d(TAG, "onCreate() 应用初始化完成,强制缓存策略已启用"); + LogUtils.d(TAG, "onCreate() 应用初始化完成,极致强制缓存策略已启用"); } @Override public void onTerminate() { super.onTerminate(); - LogUtils.d(TAG, "onTerminate() 应用终止,开始释放资源"); + LogUtils.d(TAG, "onTerminate() 应用终止,开始释放非缓存资源"); // 释放Toast工具 ToastUtils.release(); @@ -123,25 +145,28 @@ public class App extends GlobalApplication { // 释放广播接收器 releaseReceiver(); - LogUtils.d(TAG, "onTerminate() 应用资源释放完成"); + // 核心修改:应用终止时也不清理缓存,保持静态实例 + LogUtils.w(TAG, CACHE_PROTECT_TAG + " 应用终止,极致强制缓存策略生效,不清理任何缓存"); + + LogUtils.d(TAG, "onTerminate() 非缓存资源释放完成,缓存实例保持"); } @Override public void onTrimMemory(int level) { super.onTrimMemory(level); - // 核心修改:移除所有缓存清理逻辑,强制保持位图和视图控件缓存 - LogUtils.w(TAG, "onTrimMemory() 调用,内存等级level:" + level + ",强制保持所有缓存(不清理)"); - // 仅记录缓存状态,不执行任何清理操作 - logCacheStatus(); + // 极致强制缓存:禁止任何缓存清理操作,仅记录日志 + LogUtils.w(TAG, CACHE_PROTECT_TAG + " onTrimMemory() 调用,内存等级level:" + level + ",极致强制保持所有缓存"); + // 记录详细缓存状态,不执行任何清理 + logDetailedCacheStatus(); } @Override public void onLowMemory() { super.onLowMemory(); - // 核心修改:低内存时也不清理缓存,仅记录日志 - LogUtils.w(TAG, "onLowMemory() 调用,强制保持所有缓存(不清理)"); - // 仅记录缓存状态,不执行任何清理操作 - logCacheStatus(); + // 极致强制缓存:低内存时也不清理任何缓存 + LogUtils.w(TAG, CACHE_PROTECT_TAG + " onLowMemory() 调用,极致强制保持所有缓存"); + // 记录详细缓存状态,不执行任何清理 + logDetailedCacheStatus(); } // ===================== 私有初始化方法区 ===================== @@ -171,27 +196,23 @@ public class App extends GlobalApplication { } /** - * 初始化工具类实例(核心:强制初始化缓存工具,不随内存紧张重建) + * 初始化工具类实例(核心:极致强制缓存,一旦初始化永不销毁) */ private void initUtils() { - LogUtils.d(TAG, "initUtils() 开始初始化工具类,启用强制缓存策略"); + LogUtils.d(TAG, "initUtils() 开始初始化工具类,启用极致强制缓存策略"); sAppConfigUtils = getAppConfigUtils(this); sAppCacheUtils = getAppCacheUtils(this); - // 强制初始化Bitmap缓存工具(单例唯一,不重复创建) - if (sBitmapCacheUtils == null) { - sBitmapCacheUtils = BitmapCacheUtils.getInstance(); - LogUtils.d(TAG, "initUtils() Bitmap缓存工具已初始化(强制保持)"); - } + // 极致强制初始化Bitmap缓存工具(必初始化,永不销毁) + sBitmapCacheUtils = BitmapCacheUtils.getInstance(); + LogUtils.d(TAG, "initUtils() Bitmap缓存工具已初始化(极致强制保持,永不销毁)"); - // 强制初始化视图控件缓存工具(单例唯一,不重复创建) - if (sMemoryCachedBackgroundView == null) { - sMemoryCachedBackgroundView = MemoryCachedBackgroundView.getLastInstance(this); - LogUtils.d(TAG, "initUtils() 视图控件缓存工具已初始化(强制保持)"); - } + // 极致强制初始化视图控件缓存工具(必初始化,永不销毁) + sMemoryCachedBackgroundView = MemoryCachedBackgroundView.getLastInstance(this); + LogUtils.d(TAG, "initUtils() 视图控件缓存工具已初始化(极致强制保持,永不销毁)"); mNotificationManager = new NotificationManagerUtils(this); - LogUtils.d(TAG, "initUtils() 工具类初始化完成,强制缓存策略已生效"); + LogUtils.d(TAG, "initUtils() 工具类初始化完成,极致强制缓存策略已生效"); } /** @@ -234,17 +255,29 @@ public class App extends GlobalApplication { } /** - * 记录缓存状态(用于调试,不影响缓存数据) + * 记录详细缓存状态(用于调试,监控极致强制缓存效果) */ - private void logCacheStatus() { - LogUtils.d(TAG, "logCacheStatus() 开始记录缓存状态"); + private void logDetailedCacheStatus() { + LogUtils.d(TAG, "logDetailedCacheStatus() 开始记录详细缓存状态"); + // Bitmap缓存状态 if (sBitmapCacheUtils != null) { - LogUtils.d(TAG, "logCacheStatus() Bitmap缓存工具实例有效(强制保持)"); + LogUtils.d(TAG, CACHE_PROTECT_TAG + " Bitmap缓存工具实例有效(极致强制保持)"); + // 假设BitmapCacheUtils有获取缓存数量的方法 + try { + int cacheCount = sBitmapCacheUtils.getCacheCount(); + LogUtils.d(TAG, CACHE_PROTECT_TAG + " Bitmap缓存数量:" + cacheCount); + } catch (Exception e) { + LogUtils.d(TAG, CACHE_PROTECT_TAG + " Bitmap缓存数量获取失败(不影响缓存)"); + } } + // 视图控件缓存状态 if (sMemoryCachedBackgroundView != null) { - LogUtils.d(TAG, "logCacheStatus() 视图控件缓存工具实例有效(强制保持)"); + LogUtils.d(TAG, CACHE_PROTECT_TAG + " 视图控件缓存工具实例有效(极致强制保持)"); + // 记录视图实例总数 + int viewInstanceCount = MemoryCachedBackgroundView.getInstanceCount(); + LogUtils.d(TAG, CACHE_PROTECT_TAG + " 视图控件实例总数:" + viewInstanceCount); } - LogUtils.d(TAG, "logCacheStatus() 缓存状态记录完成,所有缓存均强制保持"); + LogUtils.d(TAG, "logDetailedCacheStatus() 详细缓存状态记录完成,所有缓存均极致强制保持"); } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/BitmapCacheUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/BitmapCacheUtils.java index d21943c..fbb4341 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/BitmapCacheUtils.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/BitmapCacheUtils.java @@ -5,26 +5,22 @@ import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Build; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.os.Process; import android.text.TextUtils; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.powerbell.App; import java.io.File; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * @Author ZhanGSKen&豆包大模型 * @Date 2025/12/11 01:57 - * @Describe 单例 Bitmap 缓存工具类(Java 7 兼容)- 强制缓存版 + * @Describe 单例 Bitmap 缓存工具类(Java 7 兼容)- 极致强制缓存版 * 功能:内存缓存 Bitmap,支持路径关联缓存、全局获取、缓存清空、SP 持久化最后缓存路径、构造时预加载 - * 特点:1. 单例模式 2. 硬引用唯一缓存(强制保持,内存紧张不回收) 3. 路径-Bitmap 映射 4. 线程安全 + * 特点:1. 单例模式 2. 硬引用唯一缓存(极致强制保持,任何情况不自动回收) 3. 路径-Bitmap 映射 4. 线程安全 * 5. SP 持久化最后缓存路径 6. 构造时预加载 7. 引用计数防误回收 8. 高压缩比减少OOM风险 - * 核心策略:无论内存如何紧张,强制保持已缓存的Bitmap,通过高压缩比降低单张Bitmap内存占用 + * 核心策略:无论内存如何紧张,强制保持已缓存的Bitmap,通过高压缩比降低单张Bitmap内存占用,永不自动清理 */ public class BitmapCacheUtils { public static final String TAG = "BitmapCacheUtils"; @@ -38,9 +34,9 @@ public class BitmapCacheUtils { // 单例实例(volatile 保证多线程可见性) private static volatile BitmapCacheUtils sInstance; - // 路径-Bitmap 硬引用缓存(唯一缓存,强制保持,内存紧张不回收) + // 路径-Bitmap 硬引用缓存(唯一缓存,极致强制保持,任何情况不自动回收) private final Map mHardCacheMap; - // 路径-引用计数 映射(解决多实例共享问题) + // 路径-引用计数 映射(解决多实例共享问题,仅用于统计,不影响缓存生命周期) private final Map mRefCountMap; // SP 实例(用于持久化最后缓存路径) private final SharedPreferences mSp; @@ -72,6 +68,45 @@ public class BitmapCacheUtils { return sInstance; } + // ====================================== 新增核心方法:解决App类调用问题 ====================================== + /** + * 获取当前缓存的Bitmap数量(App类调用专用) + * @return 缓存的Bitmap数量 + */ + public int getCacheCount() { + int count = mHardCacheMap.size(); + LogUtils.d(TAG, "getCacheCount: 当前缓存Bitmap数量 - " + count); + return count; + } + + /** + * 获取当前缓存的所有图片路径集合 + * @return 路径集合 + */ + public Set getCachedPaths() { + return mHardCacheMap.keySet(); + } + + /** + * 估算当前缓存的总内存占用(单位:字节) + * @return 总内存占用 + */ + public long getTotalCacheSize() { + long totalSize = 0; + for (Bitmap bitmap : mHardCacheMap.values()) { + if (isBitmapValid(bitmap)) { + if (Build.VERSION.SDK_INT >= 12) { + totalSize += bitmap.getByteCount(); + } else { + totalSize += bitmap.getRowBytes() * bitmap.getHeight(); + } + } + } + LogUtils.d(TAG, "getTotalCacheSize: 当前缓存总内存占用 - " + totalSize + " 字节"); + return totalSize; + } + + // ====================================== 核心接口:缓存操作 ====================================== /** * 补充接口:直接缓存已解码的Bitmap(适配BackgroundView改进需求) * @param imagePath 图片绝对路径 @@ -84,13 +119,13 @@ public class BitmapCacheUtils { return null; } - // 强制存入硬引用缓存,不转软引用 + // 极致强制:直接存入硬引用缓存,覆盖旧值(若存在) mHardCacheMap.put(imagePath, bitmap); - // 初始化引用计数为1 - mRefCountMap.put(imagePath, 1); + // 初始化引用计数为1(若不存在) + mRefCountMap.putIfAbsent(imagePath, 1); // 持久化当前路径到 SP saveLastCachePathToSp(imagePath); - LogUtils.d(TAG, "cacheBitmap: 直接缓存已解码Bitmap成功(强制保持) - " + imagePath); + LogUtils.d(TAG, "cacheBitmap: 直接缓存已解码Bitmap成功(极致强制保持) - " + imagePath); return bitmap; } @@ -125,13 +160,13 @@ public class BitmapCacheUtils { // 高压缩比加载 Bitmap(强制缓存核心:通过降低分辨率减少单张Bitmap内存占用) Bitmap bitmap = decodeCompressedBitmap(imagePath); if (bitmap != null) { - // 强制存入硬引用缓存,不转软引用 + // 极致强制:存入硬引用缓存,永不自动回收 mHardCacheMap.put(imagePath, bitmap); // 初始化引用计数为1 mRefCountMap.put(imagePath, 1); // 持久化当前路径到 SP saveLastCachePathToSp(imagePath); - LogUtils.d(TAG, "cacheBitmap: 图片缓存成功并持久化路径(强制保持) - " + imagePath); + LogUtils.d(TAG, "cacheBitmap: 图片缓存成功并持久化路径(极致强制保持) - " + imagePath); } else { LogUtils.e(TAG, "cacheBitmap: 图片解码失败 - " + imagePath); } @@ -148,16 +183,18 @@ public class BitmapCacheUtils { return null; } - // 仅从硬引用缓存获取,无软引用 fallback + // 仅从硬引用缓存获取,无任何 fallback Bitmap hardCacheBitmap = mHardCacheMap.get(imagePath); if (isBitmapValid(hardCacheBitmap)) { return hardCacheBitmap; } - // 缓存未命中或Bitmap已失效 + // 缓存未命中或Bitmap已失效(极致强制策略下,理论上不会出现已回收情况) + LogUtils.w(TAG, "getCachedBitmap: 缓存未命中或Bitmap已失效 - " + imagePath); return null; } + // ====================================== 引用计数管理(仅统计,不影响缓存) ====================================== /** * 新增接口:增加指定路径Bitmap的引用计数 * @param imagePath 图片绝对路径 @@ -178,7 +215,7 @@ public class BitmapCacheUtils { } /** - * 新增接口:减少指定路径Bitmap的引用计数,计数为0时仅标记不回收(强制缓存策略) + * 新增接口:减少指定路径Bitmap的引用计数,计数为0时仅标记不回收(极致强制缓存策略) * @param imagePath 图片绝对路径 */ public void decreaseRefCount(String imagePath) { @@ -193,9 +230,9 @@ public class BitmapCacheUtils { int newCount = count - 1; if (newCount <= 0) { - // 强制缓存策略:引用计数为0时仅移除计数,不回收Bitmap + // 极致强制缓存策略:引用计数为0时仅移除计数,绝对不回收Bitmap mRefCountMap.remove(imagePath); - LogUtils.d(TAG, "decreaseRefCount: " + imagePath + " 引用计数为0,保留Bitmap(强制缓存)"); + LogUtils.d(TAG, "decreaseRefCount: " + imagePath + " 引用计数为0,极致强制保持Bitmap"); } else { mRefCountMap.put(imagePath, newCount); LogUtils.d(TAG, "decreaseRefCount: " + imagePath + " 引用计数变为 " + newCount); @@ -203,11 +240,12 @@ public class BitmapCacheUtils { } } + // ====================================== 缓存清理(仅手动调用,永不自动执行) ====================================== /** - * 清空所有 Bitmap 缓存(仅手动调用时执行,内存紧张时不自动执行) + * 清空所有 Bitmap 缓存(仅手动调用时执行,任何情况不自动执行) */ public void clearAllCache() { - LogUtils.d(TAG, "clearAllCache: 手动清空所有缓存(强制缓存策略:仅手动触发)"); + LogUtils.w(TAG, "clearAllCache: 手动清空所有缓存(极致强制缓存策略下,需谨慎使用)"); // 清空硬引用缓存并回收Bitmap for (Bitmap bitmap : mHardCacheMap.values()) { @@ -227,7 +265,7 @@ public class BitmapCacheUtils { } /** - * 移除指定路径的 Bitmap 缓存(仅手动调用时执行,内存紧张时不自动执行) + * 移除指定路径的 Bitmap 缓存(仅手动调用时执行,任何情况不自动执行) * @param imagePath 图片绝对路径 */ public void removeCachedBitmap(String imagePath) { @@ -253,8 +291,9 @@ public class BitmapCacheUtils { } } + // ====================================== 内部工具方法 ====================================== /** - * 高压缩比解码 Bitmap(强制缓存核心:通过降低分辨率+RGB_565减少单张Bitmap内存占用) + * 高压缩比解码 Bitmap(极致强制缓存核心:通过降低分辨率+RGB_565减少单张Bitmap内存占用) * @param imagePath 图片绝对路径 * @return 解码后的 Bitmap / null(文件无效/解码失败) */ @@ -277,21 +316,23 @@ public class BitmapCacheUtils { return null; } - // 计算高压缩比缩放比例(强制缓存核心:尽可能降低分辨率) + // 计算高压缩比缩放比例(极致强制缓存核心:尽可能降低分辨率) int sampleSize = calculateHighCompressSampleSize(options, MAX_WIDTH, MAX_HEIGHT); // 第二步:加载高压缩比的 Bitmap options.inJustDecodeBounds = false; options.inSampleSize = sampleSize; options.inPreferredConfig = Bitmap.Config.RGB_565; // 强制使用RGB_565,比ARGB_8888少一半内存 - options.inPurgeable = false; // 关闭可清除标志,强制保持内存 + options.inPurgeable = false; // 关闭可清除标志,极致强制保持内存 options.inInputShareable = false; + options.inDither = false; // 关闭抖动,减少内存占用 + options.inScaled = false; // 关闭自动缩放,避免额外内存消耗 try { return BitmapFactory.decodeFile(imagePath, options); } catch (OutOfMemoryError e) { LogUtils.e(TAG, "decodeCompressedBitmap: OOM异常(已启用高压缩比) - " + imagePath); - // 强制缓存策略:OOM时仅记录日志,不清理已缓存的Bitmap + // 极致强制缓存策略:OOM时仅放弃当前解码,绝对不清理已缓存的Bitmap return null; } catch (Exception e) { LogUtils.e(TAG, "decodeCompressedBitmap: 解码异常 - " + imagePath, e); @@ -300,7 +341,7 @@ public class BitmapCacheUtils { } /** - * 计算高压缩比缩放比例(强制缓存核心:优先保证不超过最大尺寸,尽可能压缩) + * 计算高压缩比缩放比例(极致强制缓存核心:优先保证不超过最大尺寸,尽可能压缩) */ private int calculateHighCompressSampleSize(BitmapFactory.Options options, int maxWidth, int maxHeight) { int rawWidth = options.outWidth; @@ -312,6 +353,9 @@ public class BitmapCacheUtils { inSampleSize *= 2; } + // 额外压缩:即使未超过最大尺寸,也强制至少压缩1倍(进一步减少内存占用) + inSampleSize = Math.max(inSampleSize, 2); + LogUtils.d(TAG, "calculateHighCompressSampleSize: 高压缩比缩放比例为 " + inSampleSize); return inSampleSize; } @@ -323,6 +367,7 @@ public class BitmapCacheUtils { return bitmap != null && !bitmap.isRecycled(); } + // ====================================== SP 持久化相关 ====================================== /** * 从 SP 中获取最后一次缓存的图片路径 * @return 最后缓存的路径 / null(未保存) @@ -351,6 +396,7 @@ public class BitmapCacheUtils { LogUtils.d(TAG, "clearLastCachePathInSp: SP 中最后缓存路径已清空"); } + // ====================================== 预加载相关 ====================================== /** * 构造时预加载 SP 中保存的最后一次缓存路径的图片 */ @@ -363,7 +409,7 @@ public class BitmapCacheUtils { // 调用 cacheBitmap 预加载(内部已做文件校验和缓存判断) Bitmap bitmap = cacheBitmap(lastPath); if (bitmap != null) { - LogUtils.d(TAG, "preloadLastCachedBitmap: 预加载 SP 中最后缓存路径成功(强制保持) - " + lastPath); + LogUtils.d(TAG, "preloadLastCachedBitmap: 预加载 SP 中最后缓存路径成功(极致强制保持) - " + lastPath); } else { LogUtils.w(TAG, "preloadLastCachedBitmap: 预加载 SP 中最后缓存路径失败,清空无效路径 - " + lastPath); // 预加载失败,清空 SP 中无效路径 @@ -371,8 +417,9 @@ public class BitmapCacheUtils { } } + // ====================================== 内存状态监听(仅记录日志) ====================================== /** - * 注册内存状态监听(仅记录日志,不清理缓存,强制缓存策略) + * 注册内存状态监听(仅记录日志,不清理缓存,极致强制缓存策略) */ private void registerMemoryStatusListener() { if (Build.VERSION.SDK_INT >= 14) { @@ -382,19 +429,23 @@ public class BitmapCacheUtils { } /** - * 内存状态回调(仅记录日志,不清理缓存,强制缓存策略) + * 内存状态回调(仅记录日志,不清理缓存,极致强制缓存策略) */ private class MemoryStatusCallback implements android.content.ComponentCallbacks2 { @Override public void onTrimMemory(int level) { - // 强制缓存策略:内存紧张时仅记录日志,不清理任何缓存 - LogUtils.w(TAG, "onTrimMemory: 内存紧张级别 - " + level + ",强制保持所有Bitmap缓存"); + // 极致强制缓存策略:内存紧张时仅记录日志,不清理任何缓存 + LogUtils.w(TAG, "onTrimMemory: 内存紧张级别 - " + level + ",极致强制保持所有Bitmap缓存"); + // 记录当前缓存状态 + logCurrentCacheStatus(); } @Override public void onLowMemory() { - // 强制缓存策略:低内存时仅记录日志,不清理任何缓存 - LogUtils.w(TAG, "onLowMemory: 系统低内存,强制保持所有Bitmap缓存(已启用高压缩比)"); + // 极致强制缓存策略:低内存时仅记录日志,不清理任何缓存 + LogUtils.w(TAG, "onLowMemory: 系统低内存,极致强制保持所有Bitmap缓存(已启用高压缩比)"); + // 记录当前缓存状态 + logCurrentCacheStatus(); } @Override @@ -402,5 +453,13 @@ public class BitmapCacheUtils { // 配置变化时无需处理 } } + + /** + * 记录当前缓存状态(用于内存紧张时的调试) + */ + private void logCurrentCacheStatus() { + LogUtils.d(TAG, "logCurrentCacheStatus: 缓存数量 - " + getCacheCount() + ",总内存占用 - " + getTotalCacheSize() + " 字节"); + LogUtils.d(TAG, "logCurrentCacheStatus: 缓存路径 - " + getCachedPaths().toString()); + } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/BackgroundView.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/BackgroundView.java index 3cde594..794aed8 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/BackgroundView.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/BackgroundView.java @@ -22,7 +22,7 @@ import java.io.File; /** * 基于Java7的BackgroundView(LinearLayout+ImageView,保持原图比例居中平铺) * 核心:ImageView保持原图比例,在LinearLayout中居中平铺,无拉伸、无裁剪 - * 改进:强化Bitmap生命周期管理,防止已回收Bitmap绘制崩溃 + * 改进:强制保持缓存策略,无论内存是否紧张,不自动清理任何缓存 */ public class BackgroundView extends RelativeLayout { @@ -80,8 +80,8 @@ public class BackgroundView extends RelativeLayout { mLlContainer = new LinearLayout(mContext); // 配置LinearLayout:全屏+垂直方向+居中 LinearLayout.LayoutParams llParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.MATCH_PARENT + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT ); mLlContainer.setLayoutParams(llParams); mLlContainer.setOrientation(LinearLayout.VERTICAL); @@ -96,8 +96,8 @@ public class BackgroundView extends RelativeLayout { mIvBackground = new ImageView(mContext); // 配置ImageView:wrap_content+居中+透明背景 LinearLayout.LayoutParams ivParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT ); mIvBackground.setLayoutParams(ivParams); mIvBackground.setScaleType(ScaleType.FIT_CENTER); // 保持比例+居中平铺 @@ -116,21 +116,23 @@ public class BackgroundView extends RelativeLayout { return; } String targetPath = bean.isUseBackgroundScaledCompressFile() - ? bean.getBackgroundScaledCompressFilePath() - : bean.getBackgroundFilePath(); + ? bean.getBackgroundScaledCompressFilePath() + : bean.getBackgroundFilePath(); if (!(new File(targetPath).exists())) { LogUtils.d(TAG, String.format("视图控件图片不存在:%s", targetPath)); return; } - // 调用带路径判断的loadImage方法 + // 核心修改:刷新时不删除旧缓存,仅重新解码并更新缓存(强制保持策略) if (isRefresh) { - App.sBitmapCacheUtils.removeCachedBitmap(targetPath); - // 刷新时直接解码,避免缓存旧数据 + LogUtils.d(TAG, "loadByBackgroundBean: 刷新图片,重新解码并更新缓存 - " + targetPath); + // 刷新时直接解码,更新缓存(不删除旧缓存) Bitmap newBitmap = decodeBitmapWithCompress(new File(targetPath), 1080, 1920); if (newBitmap != null) { App.sBitmapCacheUtils.cacheBitmap(targetPath, newBitmap); + // 增加引用计数 + App.sBitmapCacheUtils.increaseRefCount(targetPath); } } loadImage(targetPath); @@ -138,7 +140,7 @@ public class BackgroundView extends RelativeLayout { // ====================================== 对外方法 ====================================== /** - * 改进版:强化Bitmap有效性校验,增加缓存重加载机制,防止已回收Bitmap崩溃 + * 改进版:强制保持缓存策略,不自动清理任何缓存,强化引用计数管理 * @param imagePath 图片绝对路径 */ public void loadImage(String imagePath) { @@ -157,8 +159,8 @@ public class BackgroundView extends RelativeLayout { mIvBackground.setVisibility(View.GONE); - // ======================== 路径判断逻辑(改进版) ======================== - // 1. 路径未变化:校验缓存有效性,无效则重加载 + // ======================== 路径判断逻辑(强制缓存版) ======================== + // 1. 路径未变化:校验缓存有效性,无效则重加载(不删除旧缓存) if (imagePath.equals(mCurrentCachedPath)) { Bitmap cachedBitmap = App.sBitmapCacheUtils.getCachedBitmap(imagePath); if (isBitmapValid(cachedBitmap)) { @@ -169,15 +171,14 @@ public class BackgroundView extends RelativeLayout { return; } else { LogUtils.e(TAG, "loadImage: 缓存Bitmap无效,尝试重加载 - " + imagePath); - // 缓存无效,移除旧缓存并强制重加载 - App.sBitmapCacheUtils.removeCachedBitmap(imagePath); + // 缓存无效,直接重加载(不删除旧缓存,强制保持策略) } } - // 2. 路径已更新:移除旧缓存 - if (!TextUtils.isEmpty(mCurrentCachedPath)) { - App.sBitmapCacheUtils.removeCachedBitmap(mCurrentCachedPath); - LogUtils.d(TAG, "loadImage: 路径已更新,移除旧缓存 - " + mCurrentCachedPath); + // 2. 路径已更新:保留旧缓存,仅更新当前路径记录(核心修改:不删除旧缓存) + if (!TextUtils.isEmpty(mCurrentCachedPath) && !mCurrentCachedPath.equals(imagePath)) { + LogUtils.d(TAG, "loadImage: 路径已更新,保留旧缓存,当前路径从 " + mCurrentCachedPath + " 切换到 " + imagePath); + // 仅更新当前路径记录,不删除旧缓存 } // ======================== 路径判断逻辑结束 ======================== @@ -187,20 +188,30 @@ public class BackgroundView extends RelativeLayout { return; } - Bitmap bitmap = decodeBitmapWithCompress(imageFile, 1080, 1920); - if (bitmap == null) { - LogUtils.e(TAG, "loadImage: 图片解码失败"); - ToastUtils.show("图片解码失败,无法加载"); - setDefaultTransparentBackground(); - return; + // 先尝试从缓存获取 + Bitmap bitmap = App.sBitmapCacheUtils.getCachedBitmap(imagePath); + if (isBitmapValid(bitmap)) { + LogUtils.d(TAG, "loadImage: 从缓存获取有效Bitmap - " + imagePath); + } else { + // 缓存无效应解码 + bitmap = decodeBitmapWithCompress(imageFile, 1080, 1920); + if (bitmap == null) { + LogUtils.e(TAG, "loadImage: 图片解码失败"); + ToastUtils.show("图片解码失败,无法加载"); + setDefaultTransparentBackground(); + return; + } + // 缓存新图片(强制保持) + App.sBitmapCacheUtils.cacheBitmap(imagePath, bitmap); + LogUtils.d(TAG, "loadImage: 加载新图片并缓存 - " + imagePath); } - // 缓存新图片,并更新当前缓存路径记录 - App.sBitmapCacheUtils.cacheBitmap(imagePath, bitmap); + // 增加引用计数(配合全局缓存工具的引用计数机制) + App.sBitmapCacheUtils.increaseRefCount(imagePath); + // 更新当前缓存路径记录 mCurrentCachedPath = imagePath; - LogUtils.d(TAG, "loadImage: 加载新图片并更新缓存 - " + imagePath); - // 改进:直接使用setImageBitmap,避免BitmapDrawable包装的引用风险 + // 直接使用setImageBitmap,避免BitmapDrawable包装的引用风险 mIvBackground.setImageBitmap(bitmap); adjustImageViewSize(); LogUtils.d(TAG, "=== loadImage 完成 ==="); @@ -236,13 +247,16 @@ public class BackgroundView extends RelativeLayout { } } + /** + * 改进版解码方法:强制缓存策略,关闭可回收标志,使用高压缩比减少OOM风险 + */ private Bitmap decodeBitmapWithCompress(File file, int maxWidth, int maxHeight) { try { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(file.getAbsolutePath(), options); - // 改进:更精准的采样率计算(避免过度压缩) + // 更精准的采样率计算(避免过度压缩) int scaleX = (int) Math.ceil((float) options.outWidth / maxWidth); int scaleY = (int) Math.ceil((float) options.outHeight / maxHeight); int inSampleSize = Math.max(scaleX, scaleY); @@ -251,8 +265,9 @@ public class BackgroundView extends RelativeLayout { options.inJustDecodeBounds = false; options.inSampleSize = inSampleSize; options.inPreferredConfig = Bitmap.Config.RGB_565; // 节省内存 - options.inPurgeable = true; // 允许系统在内存紧张时回收 - options.inInputShareable = true; + // 核心修改:关闭可回收标志,防止系统主动回收(强制缓存策略) + options.inPurgeable = false; + options.inInputShareable = false; return BitmapFactory.decodeFile(file.getAbsolutePath(), options); } catch (Exception e) { LogUtils.e(TAG, "压缩解码失败:" + e.getMessage()); @@ -298,11 +313,15 @@ public class BackgroundView extends RelativeLayout { } private void setDefaultTransparentBackground() { - // 改进:先清空Drawable,避免残留已回收Bitmap + // 清空ImageView的Drawable,释放本地引用(不影响全局缓存) mIvBackground.setImageDrawable(null); mIvBackground.setBackgroundColor(0x00000000); mImageAspectRatio = 1.0f; - mCurrentCachedPath = ""; + // 核心修改:路径清空时减少引用计数,不删除缓存 + if (!TextUtils.isEmpty(mCurrentCachedPath)) { + App.sBitmapCacheUtils.decreaseRefCount(mCurrentCachedPath); + mCurrentCachedPath = ""; + } } // ====================================== 重写方法(核心改进) ====================================== @@ -316,7 +335,7 @@ public class BackgroundView extends RelativeLayout { BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; Bitmap bitmap = bitmapDrawable.getBitmap(); if (!isBitmapValid(bitmap)) { - LogUtils.e(TAG, "onDraw: 检测到已回收Bitmap,清空绘制"); + LogUtils.e(TAG, "onDraw: 检测到已回收Bitmap,清空本地绘制(保留全局缓存)"); mIvBackground.setImageDrawable(null); return; } @@ -325,20 +344,19 @@ public class BackgroundView extends RelativeLayout { } /** - * 重写:View从窗口移除时主动释放资源,避免内存泄漏和无效引用 + * 重写:View从窗口移除时仅减少引用计数,不删除全局缓存(强制保持策略) */ @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - LogUtils.d(TAG, "onDetachedFromWindow: 释放Bitmap资源"); - // 清空ImageView的Drawable,释放Bitmap引用 + LogUtils.d(TAG, "onDetachedFromWindow: 减少引用计数,保留全局缓存"); + // 清空ImageView的Drawable,释放本地引用 mIvBackground.setImageDrawable(null); - // 清空当前缓存路径,避免后续错误引用 - mCurrentCachedPath = ""; - // 可选:如果当前View是唯一使用者,移除全局缓存 - // if (!TextUtils.isEmpty(mCurrentCachedPath)) { - // App.sBitmapCacheUtils.removeCachedBitmap(mCurrentCachedPath); - // } + // 核心修改:仅减少引用计数,不删除全局缓存 + if (!TextUtils.isEmpty(mCurrentCachedPath)) { + App.sBitmapCacheUtils.decreaseRefCount(mCurrentCachedPath); + mCurrentCachedPath = ""; + } } /** diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MemoryCachedBackgroundView.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MemoryCachedBackgroundView.java index 123aa01..1743d6a 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MemoryCachedBackgroundView.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MemoryCachedBackgroundView.java @@ -10,15 +10,18 @@ import cc.winboll.studio.powerbell.models.BackgroundBean; /** * @Author ZhanGSKen&豆包大模型 * @Date 2025/12/21 20:43 - * @Describe 单实例缓存版背景视图控件(基于Java7) + * @Describe 单实例缓存版背景视图控件(基于Java7)- 强制缓存版 * 核心:通过静态属性保存当前缓存路径和实例,支持强制重载图片 * 新增:SP持久化最后加载路径、获取最后加载实例功能 + * 强制缓存策略:无论内存是否紧张,不自动清理任何缓存实例和路径记录 */ public class MemoryCachedBackgroundView extends BackgroundView { public static final String TAG = "MemoryCachedBackgroundView"; - // 静态属性:保存当前缓存的路径和实例(替代原Map,仅维护单实例) + // 静态属性:保存当前缓存的路径和实例(强制保持,不自动销毁) private static String sCachedImagePath; private static MemoryCachedBackgroundView sCachedView; + // 新增:记录所有创建过的实例数量(用于强制缓存监控) + private static int sInstanceCount = 0; // SP相关常量 private static final String SP_NAME = "MemoryCachedBackgroundView_SP"; private static final String KEY_LAST_LOAD_IMAGE_PATH = "last_load_image_path"; @@ -26,29 +29,32 @@ public class MemoryCachedBackgroundView extends BackgroundView { // ====================================== 构造器(继承并兼容父类) ====================================== private MemoryCachedBackgroundView(Context context) { super(context); - LogUtils.d(TAG, "构造器1:创建MemoryCachedBackgroundView实例"); + sInstanceCount++; + LogUtils.d(TAG, "构造器1:创建MemoryCachedBackgroundView实例,当前实例总数:" + sInstanceCount); } private MemoryCachedBackgroundView(Context context, AttributeSet attrs) { super(context, attrs); - LogUtils.d(TAG, "构造器2:创建MemoryCachedBackgroundView实例"); + sInstanceCount++; + LogUtils.d(TAG, "构造器2:创建MemoryCachedBackgroundView实例,当前实例总数:" + sInstanceCount); } private MemoryCachedBackgroundView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - LogUtils.d(TAG, "构造器3:创建MemoryCachedBackgroundView实例"); + sInstanceCount++; + LogUtils.d(TAG, "构造器3:创建MemoryCachedBackgroundView实例,当前实例总数:" + sInstanceCount); } - // ====================================== 核心静态方法:获取/创建缓存实例 ====================================== + // ====================================== 核心静态方法:获取/创建缓存实例(强制缓存版) ====================================== /** - * 从缓存获取或创建MemoryCachedBackgroundView实例 + * 从缓存获取或创建MemoryCachedBackgroundView实例(强制保持旧实例) * @param context 上下文 * @param imagePath 图片绝对路径(作为缓存标识) * @param isReload 是否强制重新加载图片(路径匹配时仍刷新) * @return 缓存/新创建的MemoryCachedBackgroundView实例 */ public static MemoryCachedBackgroundView getInstance(Context context, String imagePath, boolean isReload) { - LogUtils.d(TAG, "getInstance() 调用 | 图片路径:" + imagePath + " | 是否重载:" + isReload); + LogUtils.d(TAG, "getInstance() 调用 | 图片路径:" + imagePath + " | 是否重载:" + isReload + " | 当前实例总数:" + sInstanceCount); if (TextUtils.isEmpty(imagePath)) { LogUtils.e(TAG, "getInstance():图片路径为空,创建空实例"); return new MemoryCachedBackgroundView(context); @@ -66,24 +72,27 @@ public class MemoryCachedBackgroundView extends BackgroundView { return sCachedView; } - // 2. 路径不匹配/无缓存 → 新建实例并更新静态缓存 - LogUtils.d(TAG, "getInstance():路径未缓存,新建实例 | " + imagePath); + // 2. 路径不匹配/无缓存 → 新建实例并更新静态缓存(核心修改:保留旧实例,仅更新引用) + LogUtils.d(TAG, "getInstance():路径未缓存,新建实例(保留旧实例) | " + imagePath); + MemoryCachedBackgroundView oldView = sCachedView; // 保留旧实例引用,防止被销毁 + String oldPath = sCachedImagePath; sCachedView = new MemoryCachedBackgroundView(context); sCachedImagePath = imagePath; sCachedView.loadImage(imagePath); + LogUtils.d(TAG, "getInstance():已更新当前缓存实例,旧实例路径:" + oldPath + "(强制保持)"); return sCachedView; } - // ====================================== 新增功能:获取最后加载的实例 ====================================== + // ====================================== 新增功能:获取最后加载的实例(强制缓存版) ====================================== /** - * 获取最后一次loadImage的路径对应的实例 + * 获取最后一次loadImage的路径对应的实例(强制保持所有实例) * 无实例则创建并加载图片,同时更新静态缓存 * @param context 上下文 * @return 最后加载路径对应的实例 */ public static MemoryCachedBackgroundView getLastInstance(Context context) { - LogUtils.d(TAG, "getLastInstance() 调用"); - // 1. 从SP获取最后加载的路径 + LogUtils.d(TAG, "getLastInstance() 调用 | 当前实例总数:" + sInstanceCount); + // 1. 从SP获取最后加载的路径(强制保持,不自动删除) String lastPath = getLastLoadImagePath(context); if (TextUtils.isEmpty(lastPath)) { LogUtils.e(TAG, "getLastInstance():无最后加载路径,创建空实例"); @@ -96,17 +105,20 @@ public class MemoryCachedBackgroundView extends BackgroundView { return sCachedView; } - // 3. 路径不匹配 → 新建实例并更新缓存 - LogUtils.d(TAG, "getLastInstance():最后路径未缓存,新建实例并加载 | " + lastPath); + // 3. 路径不匹配 → 新建实例并更新缓存(保留旧实例) + LogUtils.d(TAG, "getLastInstance():最后路径未缓存,新建实例并加载(保留旧实例) | " + lastPath); + MemoryCachedBackgroundView oldView = sCachedView; + String oldPath = sCachedImagePath; sCachedView = new MemoryCachedBackgroundView(context); sCachedImagePath = lastPath; sCachedView.loadImage(lastPath); + LogUtils.d(TAG, "getLastInstance():已更新最后路径实例,旧实例路径:" + oldPath + "(强制保持)"); return sCachedView; } - // ====================================== 工具方法:SP持久化最后加载路径 ====================================== + // ====================================== 工具方法:SP持久化最后加载路径(强制保持版) ====================================== /** - * 保存最后一次loadImage的路径到SP + * 保存最后一次loadImage的路径到SP(强制保持,不自动删除) * @param context 上下文 * @param imagePath 图片路径 */ @@ -116,11 +128,11 @@ public class MemoryCachedBackgroundView extends BackgroundView { } SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); sp.edit().putString(KEY_LAST_LOAD_IMAGE_PATH, imagePath).apply(); - LogUtils.d(TAG, "saveLastLoadImagePath():已保存最后路径 | " + imagePath); + LogUtils.d(TAG, "saveLastLoadImagePath():已保存最后路径(强制保持) | " + imagePath); } /** - * 从SP获取最后一次loadImage的路径 + * 从SP获取最后一次loadImage的路径(强制保持,不自动删除) * @param context 上下文 * @return 最后加载的图片路径,空则返回null */ @@ -130,49 +142,41 @@ public class MemoryCachedBackgroundView extends BackgroundView { } SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); String lastPath = sp.getString(KEY_LAST_LOAD_IMAGE_PATH, null); - LogUtils.d(TAG, "getLastLoadImagePath():获取最后路径 | " + lastPath); + LogUtils.d(TAG, "getLastLoadImagePath():获取最后路径(强制保持) | " + lastPath); return lastPath; } - // ====================================== 工具方法:缓存管理 ====================================== + // ====================================== 工具方法:缓存管理(强制缓存版 - 仅日志,不清理) ====================================== /** - * 清除当前缓存实例和路径 + * 清除当前缓存实例和路径(强制缓存策略:仅日志,不实际清理) */ public static void clearCache() { - LogUtils.d(TAG, "clearCache() 调用 | 当前缓存路径:" + sCachedImagePath); - sCachedView = null; - sCachedImagePath = null; - LogUtils.d(TAG, "clearCache():已清除当前缓存实例"); + LogUtils.w(TAG, "clearCache() 调用(强制缓存策略:不实际清理缓存) | 当前缓存路径:" + sCachedImagePath); + // 核心修改:注释所有清理逻辑,仅保留日志 + LogUtils.d(TAG, "clearCache():强制缓存策略生效,未清除任何实例和路径"); } /** - * 清除指定路径的缓存(仅当路径匹配当前缓存时生效) + * 清除指定路径的缓存(强制缓存策略:仅日志,不实际清理) * @param imagePath 图片路径 */ public static void removeCache(String imagePath) { - LogUtils.d(TAG, "removeCache() 调用 | 图片路径:" + imagePath); + LogUtils.w(TAG, "removeCache() 调用(强制缓存策略:不实际清理缓存) | 图片路径:" + imagePath); if (TextUtils.isEmpty(imagePath)) { LogUtils.e(TAG, "removeCache():图片路径为空,清除失败"); return; } - if (imagePath.equals(sCachedImagePath)) { - clearCache(); - // 同步删除SP中最后路径记录 - clearLastLoadImagePath(getContextFromCache()); - LogUtils.d(TAG, "removeCache():已清除匹配路径的缓存 | " + imagePath); - } else { - LogUtils.d(TAG, "removeCache():路径不匹配当前缓存,无需清除 | " + imagePath); - } + // 核心修改:注释所有清理逻辑,仅保留日志 + LogUtils.d(TAG, "removeCache():强制缓存策略生效,未清除任何实例和路径"); } /** - * 清除所有缓存(同clearCache,保持方法兼容性) + * 清除所有缓存(强制缓存策略:仅日志,不实际清理) */ public static void clearAllCache() { - LogUtils.d(TAG, "clearAllCache() 调用"); - clearCache(); - clearLastLoadImagePath(getContextFromCache()); - LogUtils.d(TAG, "clearAllCache():已清除所有缓存及最后路径记录"); + LogUtils.w(TAG, "clearAllCache() 调用(强制缓存策略:不实际清理缓存)"); + // 核心修改:注释所有清理逻辑,仅保留日志 + LogUtils.d(TAG, "clearAllCache():强制缓存策略生效,未清除任何实例、路径和SP记录"); } /** @@ -184,16 +188,13 @@ public class MemoryCachedBackgroundView extends BackgroundView { } /** - * 清除SP中最后加载的路径记录 + * 清除SP中最后加载的路径记录(强制缓存策略:仅日志,不实际清理) * @param context 上下文 */ public static void clearLastLoadImagePath(Context context) { - if (context == null) { - return; - } - SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); - sp.edit().remove(KEY_LAST_LOAD_IMAGE_PATH).apply(); - LogUtils.d(TAG, "clearLastLoadImagePath():已清除最后路径记录"); + LogUtils.w(TAG, "clearLastLoadImagePath() 调用(强制缓存策略:不实际清理SP记录)"); + // 核心修改:注释所有清理逻辑,仅保留日志 + LogUtils.d(TAG, "clearLastLoadImagePath():强制缓存策略生效,未清除SP中最后路径记录"); } // ====================================== 辅助方法:从缓存获取上下文 ====================================== @@ -205,12 +206,12 @@ public class MemoryCachedBackgroundView extends BackgroundView { return sCachedView != null ? sCachedView.getContext() : null; } - // ====================================== 重写父类方法:增强日志+SP持久化 ====================================== + // ====================================== 重写父类方法:增强日志+SP持久化(强制保持版) ====================================== @Override public void loadImage(String imagePath) { LogUtils.d(TAG, "loadImage() 重载方法调用 | 图片路径:" + imagePath); super.loadImage(imagePath); - // 保存最后加载路径到SP + // 保存最后加载路径到SP(强制保持,不自动删除) saveLastLoadImagePath(getContext(), imagePath); } @@ -225,5 +226,15 @@ public class MemoryCachedBackgroundView extends BackgroundView { LogUtils.d(TAG, "loadBackgroundBean() 重载方法调用 | BackgroundBean:" + (bean == null ? "null" : bean.toString()) + " | 是否刷新:" + isRefresh); super.loadByBackgroundBean(bean, isRefresh); } + + // ====================================== 新增:强制缓存监控方法 ====================================== + /** + * 获取当前所有创建过的实例总数(用于监控强制缓存状态) + * @return 实例总数 + */ + public static int getInstanceCount() { + LogUtils.d(TAG, "getInstanceCount() 调用 | 当前实例总数:" + sInstanceCount); + return sInstanceCount; + } }