Compare commits
2 Commits
powerbell-
...
powerbell-
| Author | SHA1 | Date | |
|---|---|---|---|
| 26b86cd715 | |||
| 7888696e65 |
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Mon Dec 22 11:02:54 HKT 2025
|
||||
stageCount=22
|
||||
#Mon Dec 22 11:21:24 HKT 2025
|
||||
stageCount=23
|
||||
libraryProject=
|
||||
baseVersion=15.14
|
||||
publishVersion=15.14.21
|
||||
publishVersion=15.14.22
|
||||
buildCount=0
|
||||
baseBetaVersion=15.14.22
|
||||
baseBetaVersion=15.14.23
|
||||
|
||||
@@ -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() 详细缓存状态记录完成,所有缓存均极致强制保持");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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&豆包大模型<zhangsken@qq.com>
|
||||
* @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<String, Bitmap> mHardCacheMap;
|
||||
// 路径-引用计数 映射(解决多实例共享问题)
|
||||
// 路径-引用计数 映射(解决多实例共享问题,仅用于统计,不影响缓存生命周期)
|
||||
private final Map<String, Integer> 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<String> 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,15 +10,18 @@ import cc.winboll.studio.powerbell.models.BackgroundBean;
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user