Compare commits
2 Commits
powerbell-
...
powerbell-
| Author | SHA1 | Date | |
|---|---|---|---|
| c38b392e39 | |||
| 0f736c2007 |
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Mon Dec 22 11:21:24 HKT 2025
|
#Mon Dec 22 13:00:54 HKT 2025
|
||||||
stageCount=23
|
stageCount=24
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.14
|
baseVersion=15.14
|
||||||
publishVersion=15.14.22
|
publishVersion=15.14.23
|
||||||
buildCount=0
|
buildCount=0
|
||||||
baseBetaVersion=15.14.23
|
baseBetaVersion=15.14.24
|
||||||
|
|||||||
@@ -16,17 +16,16 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||||
* @Date 2025/12/11 01:57
|
* @Date 2025/12/11 01:57
|
||||||
* @Describe 单例 Bitmap 缓存工具类(Java 7 兼容)- 极致强制缓存版
|
* @Describe 单例 Bitmap 缓存工具类(Java 7 兼容)- 极致强制缓存版(无图片压缩)
|
||||||
* 功能:内存缓存 Bitmap,支持路径关联缓存、全局获取、缓存清空、SP 持久化最后缓存路径、构造时预加载
|
* 功能:内存缓存 Bitmap,支持路径关联缓存、全局获取、缓存清空、SP 持久化最后缓存路径、构造时预加载
|
||||||
* 特点:1. 单例模式 2. 硬引用唯一缓存(极致强制保持,任何情况不自动回收) 3. 路径-Bitmap 映射 4. 线程安全
|
* 特点:1. 单例模式 2. 硬引用唯一缓存(极致强制保持,任何情况不自动回收) 3. 路径-Bitmap 映射 4. 线程安全
|
||||||
* 5. SP 持久化最后缓存路径 6. 构造时预加载 7. 引用计数防误回收 8. 高压缩比减少OOM风险
|
* 5. SP 持久化最后缓存路径 6. 构造时预加载 7. 引用计数防误回收 8. 无图片压缩,保留原始品质
|
||||||
* 核心策略:无论内存如何紧张,强制保持已缓存的Bitmap,通过高压缩比降低单张Bitmap内存占用,永不自动清理
|
* 核心策略:无论内存如何紧张,强制保持已缓存的Bitmap,保留图片原始品质,永不自动清理
|
||||||
*/
|
*/
|
||||||
public class BitmapCacheUtils {
|
public class BitmapCacheUtils {
|
||||||
public static final String TAG = "BitmapCacheUtils";
|
public static final String TAG = "BitmapCacheUtils";
|
||||||
// 最大图片尺寸(降低至720P,进一步减少内存占用,强制缓存核心策略)
|
// 移除最大图片尺寸限制(不压缩图片)
|
||||||
private static final int MAX_WIDTH = 720;
|
// 若需限制尺寸而非压缩品质,可保留此常量用于校验,不参与采样率计算
|
||||||
private static final int MAX_HEIGHT = 1280;
|
|
||||||
|
|
||||||
// SP 相关常量
|
// SP 相关常量
|
||||||
private static final String SP_NAME = "BitmapCacheSP";
|
private static final String SP_NAME = "BitmapCacheSP";
|
||||||
@@ -68,7 +67,7 @@ public class BitmapCacheUtils {
|
|||||||
return sInstance;
|
return sInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================================== 新增核心方法:解决App类调用问题 ======================================
|
// ====================================== 保留核心监控方法:保证与App类兼容 ======================================
|
||||||
/**
|
/**
|
||||||
* 获取当前缓存的Bitmap数量(App类调用专用)
|
* 获取当前缓存的Bitmap数量(App类调用专用)
|
||||||
* @return 缓存的Bitmap数量
|
* @return 缓存的Bitmap数量
|
||||||
@@ -106,7 +105,7 @@ public class BitmapCacheUtils {
|
|||||||
return totalSize;
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================================== 核心接口:缓存操作 ======================================
|
// ====================================== 核心接口:缓存操作(无压缩) ======================================
|
||||||
/**
|
/**
|
||||||
* 补充接口:直接缓存已解码的Bitmap(适配BackgroundView改进需求)
|
* 补充接口:直接缓存已解码的Bitmap(适配BackgroundView改进需求)
|
||||||
* @param imagePath 图片绝对路径
|
* @param imagePath 图片绝对路径
|
||||||
@@ -125,7 +124,7 @@ public class BitmapCacheUtils {
|
|||||||
mRefCountMap.putIfAbsent(imagePath, 1);
|
mRefCountMap.putIfAbsent(imagePath, 1);
|
||||||
// 持久化当前路径到 SP
|
// 持久化当前路径到 SP
|
||||||
saveLastCachePathToSp(imagePath);
|
saveLastCachePathToSp(imagePath);
|
||||||
LogUtils.d(TAG, "cacheBitmap: 直接缓存已解码Bitmap成功(极致强制保持) - " + imagePath);
|
LogUtils.d(TAG, "cacheBitmap: 直接缓存已解码Bitmap成功(极致强制保持,无压缩) - " + imagePath);
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,8 +156,8 @@ public class BitmapCacheUtils {
|
|||||||
return hardCacheBitmap;
|
return hardCacheBitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 高压缩比加载 Bitmap(强制缓存核心:通过降低分辨率减少单张Bitmap内存占用)
|
// 无压缩解码 Bitmap(保留原始品质)
|
||||||
Bitmap bitmap = decodeCompressedBitmap(imagePath);
|
Bitmap bitmap = decodeOriginalBitmap(imagePath);
|
||||||
if (bitmap != null) {
|
if (bitmap != null) {
|
||||||
// 极致强制:存入硬引用缓存,永不自动回收
|
// 极致强制:存入硬引用缓存,永不自动回收
|
||||||
mHardCacheMap.put(imagePath, bitmap);
|
mHardCacheMap.put(imagePath, bitmap);
|
||||||
@@ -166,7 +165,7 @@ public class BitmapCacheUtils {
|
|||||||
mRefCountMap.put(imagePath, 1);
|
mRefCountMap.put(imagePath, 1);
|
||||||
// 持久化当前路径到 SP
|
// 持久化当前路径到 SP
|
||||||
saveLastCachePathToSp(imagePath);
|
saveLastCachePathToSp(imagePath);
|
||||||
LogUtils.d(TAG, "cacheBitmap: 图片缓存成功并持久化路径(极致强制保持) - " + imagePath);
|
LogUtils.d(TAG, "cacheBitmap: 图片缓存成功并持久化路径(极致强制保持,无压缩) - " + imagePath);
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e(TAG, "cacheBitmap: 图片解码失败 - " + imagePath);
|
LogUtils.e(TAG, "cacheBitmap: 图片解码失败 - " + imagePath);
|
||||||
}
|
}
|
||||||
@@ -291,75 +290,54 @@ public class BitmapCacheUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================================== 内部工具方法 ======================================
|
// ====================================== 内部工具方法(无压缩解码) ======================================
|
||||||
/**
|
/**
|
||||||
* 高压缩比解码 Bitmap(极致强制缓存核心:通过降低分辨率+RGB_565减少单张Bitmap内存占用)
|
* 无压缩解码 Bitmap(保留原始品质)
|
||||||
* @param imagePath 图片绝对路径
|
* @param imagePath 图片绝对路径
|
||||||
* @return 解码后的 Bitmap / null(文件无效/解码失败)
|
* @return 解码后的 Bitmap / null(文件无效/解码失败)
|
||||||
*/
|
*/
|
||||||
private Bitmap decodeCompressedBitmap(String imagePath) {
|
private Bitmap decodeOriginalBitmap(String imagePath) {
|
||||||
// 前置校验:确保文件有效
|
// 前置校验:确保文件有效
|
||||||
File imageFile = new File(imagePath);
|
File imageFile = new File(imagePath);
|
||||||
if (!imageFile.exists() || !imageFile.isFile() || imageFile.length() <= 0) {
|
if (!imageFile.exists() || !imageFile.isFile() || imageFile.length() <= 0) {
|
||||||
LogUtils.e(TAG, "decodeCompressedBitmap: 文件无效,跳过解码 - " + imagePath);
|
LogUtils.e(TAG, "decodeOriginalBitmap: 文件无效,跳过解码 - " + imagePath);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
// 第一步:只获取图片尺寸,不加载像素
|
// 仅获取尺寸用于日志记录,不参与解码逻辑
|
||||||
options.inJustDecodeBounds = true;
|
options.inJustDecodeBounds = true;
|
||||||
BitmapFactory.decodeFile(imagePath, options);
|
BitmapFactory.decodeFile(imagePath, options);
|
||||||
|
|
||||||
// 校验尺寸是否有效
|
// 校验尺寸是否有效
|
||||||
if (options.outWidth <= 0 || options.outHeight <= 0) {
|
if (options.outWidth <= 0 || options.outHeight <= 0) {
|
||||||
LogUtils.e(TAG, "decodeCompressedBitmap: 图片尺寸无效 - " + imagePath);
|
LogUtils.e(TAG, "decodeOriginalBitmap: 图片尺寸无效 - " + imagePath);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算高压缩比缩放比例(极致强制缓存核心:尽可能降低分辨率)
|
LogUtils.d(TAG, "decodeOriginalBitmap: 图片原始尺寸 - " + options.outWidth + "x" + options.outHeight);
|
||||||
int sampleSize = calculateHighCompressSampleSize(options, MAX_WIDTH, MAX_HEIGHT);
|
|
||||||
|
|
||||||
// 第二步:加载高压缩比的 Bitmap
|
// 无压缩解码配置
|
||||||
options.inJustDecodeBounds = false;
|
options.inJustDecodeBounds = false;
|
||||||
options.inSampleSize = sampleSize;
|
options.inSampleSize = 1; // 不缩放,采样率为1
|
||||||
options.inPreferredConfig = Bitmap.Config.RGB_565; // 强制使用RGB_565,比ARGB_8888少一半内存
|
options.inPreferredConfig = Bitmap.Config.ARGB_8888; // 保留全彩品质(如需节省内存可改为RGB_565,不影响品质)
|
||||||
options.inPurgeable = false; // 关闭可清除标志,极致强制保持内存
|
options.inPurgeable = false; // 关闭可清除标志,极致强制保持内存
|
||||||
options.inInputShareable = false;
|
options.inInputShareable = false;
|
||||||
options.inDither = false; // 关闭抖动,减少内存占用
|
options.inDither = true; // 开启抖动,保证色彩还原
|
||||||
options.inScaled = false; // 关闭自动缩放,避免额外内存消耗
|
options.inScaled = false; // 关闭自动缩放,保留原始尺寸
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return BitmapFactory.decodeFile(imagePath, options);
|
return BitmapFactory.decodeFile(imagePath, options);
|
||||||
} catch (OutOfMemoryError e) {
|
} catch (OutOfMemoryError e) {
|
||||||
LogUtils.e(TAG, "decodeCompressedBitmap: OOM异常(已启用高压缩比) - " + imagePath);
|
LogUtils.e(TAG, "decodeOriginalBitmap: OOM异常(无压缩,图片尺寸过大) - " + imagePath);
|
||||||
// 极致强制缓存策略:OOM时仅放弃当前解码,绝对不清理已缓存的Bitmap
|
// 极致强制缓存策略:OOM时仅放弃当前解码,绝对不清理已缓存的Bitmap
|
||||||
return null;
|
return null;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LogUtils.e(TAG, "decodeCompressedBitmap: 解码异常 - " + imagePath, e);
|
LogUtils.e(TAG, "decodeOriginalBitmap: 解码异常 - " + imagePath, e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算高压缩比缩放比例(极致强制缓存核心:优先保证不超过最大尺寸,尽可能压缩)
|
|
||||||
*/
|
|
||||||
private int calculateHighCompressSampleSize(BitmapFactory.Options options, int maxWidth, int maxHeight) {
|
|
||||||
int rawWidth = options.outWidth;
|
|
||||||
int rawHeight = options.outHeight;
|
|
||||||
int inSampleSize = 1;
|
|
||||||
|
|
||||||
// 高压缩比逻辑:只要超过最大尺寸,就持续放大采样率
|
|
||||||
while (rawWidth / inSampleSize > maxWidth || rawHeight / inSampleSize > maxHeight) {
|
|
||||||
inSampleSize *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 额外压缩:即使未超过最大尺寸,也强制至少压缩1倍(进一步减少内存占用)
|
|
||||||
inSampleSize = Math.max(inSampleSize, 2);
|
|
||||||
|
|
||||||
LogUtils.d(TAG, "calculateHighCompressSampleSize: 高压缩比缩放比例为 " + inSampleSize);
|
|
||||||
return inSampleSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工具方法:判断Bitmap是否有效(非空且未被回收)
|
* 工具方法:判断Bitmap是否有效(非空且未被回收)
|
||||||
*/
|
*/
|
||||||
@@ -409,7 +387,7 @@ public class BitmapCacheUtils {
|
|||||||
// 调用 cacheBitmap 预加载(内部已做文件校验和缓存判断)
|
// 调用 cacheBitmap 预加载(内部已做文件校验和缓存判断)
|
||||||
Bitmap bitmap = cacheBitmap(lastPath);
|
Bitmap bitmap = cacheBitmap(lastPath);
|
||||||
if (bitmap != null) {
|
if (bitmap != null) {
|
||||||
LogUtils.d(TAG, "preloadLastCachedBitmap: 预加载 SP 中最后缓存路径成功(极致强制保持) - " + lastPath);
|
LogUtils.d(TAG, "preloadLastCachedBitmap: 预加载 SP 中最后缓存路径成功(极致强制保持,无压缩) - " + lastPath);
|
||||||
} else {
|
} else {
|
||||||
LogUtils.w(TAG, "preloadLastCachedBitmap: 预加载 SP 中最后缓存路径失败,清空无效路径 - " + lastPath);
|
LogUtils.w(TAG, "preloadLastCachedBitmap: 预加载 SP 中最后缓存路径失败,清空无效路径 - " + lastPath);
|
||||||
// 预加载失败,清空 SP 中无效路径
|
// 预加载失败,清空 SP 中无效路径
|
||||||
@@ -435,7 +413,7 @@ public class BitmapCacheUtils {
|
|||||||
@Override
|
@Override
|
||||||
public void onTrimMemory(int level) {
|
public void onTrimMemory(int level) {
|
||||||
// 极致强制缓存策略:内存紧张时仅记录日志,不清理任何缓存
|
// 极致强制缓存策略:内存紧张时仅记录日志,不清理任何缓存
|
||||||
LogUtils.w(TAG, "onTrimMemory: 内存紧张级别 - " + level + ",极致强制保持所有Bitmap缓存");
|
LogUtils.w(TAG, "onTrimMemory: 内存紧张级别 - " + level + ",极致强制保持所有Bitmap缓存(无压缩)");
|
||||||
// 记录当前缓存状态
|
// 记录当前缓存状态
|
||||||
logCurrentCacheStatus();
|
logCurrentCacheStatus();
|
||||||
}
|
}
|
||||||
@@ -443,7 +421,7 @@ public class BitmapCacheUtils {
|
|||||||
@Override
|
@Override
|
||||||
public void onLowMemory() {
|
public void onLowMemory() {
|
||||||
// 极致强制缓存策略:低内存时仅记录日志,不清理任何缓存
|
// 极致强制缓存策略:低内存时仅记录日志,不清理任何缓存
|
||||||
LogUtils.w(TAG, "onLowMemory: 系统低内存,极致强制保持所有Bitmap缓存(已启用高压缩比)");
|
LogUtils.w(TAG, "onLowMemory: 系统低内存,极致强制保持所有Bitmap缓存(无压缩)");
|
||||||
// 记录当前缓存状态
|
// 记录当前缓存状态
|
||||||
logCurrentCacheStatus();
|
logCurrentCacheStatus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import java.io.File;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 基于Java7的BackgroundView(LinearLayout+ImageView,保持原图比例居中平铺)
|
* 基于Java7的BackgroundView(LinearLayout+ImageView,保持原图比例居中平铺)
|
||||||
* 核心:ImageView保持原图比例,在LinearLayout中居中平铺,无拉伸、无裁剪
|
* 核心:ImageView保持原图比例,在LinearLayout中居中平铺,无拉伸、无裁剪、无压缩
|
||||||
* 改进:强制保持缓存策略,无论内存是否紧张,不自动清理任何缓存
|
* 改进:强制保持缓存策略,无论内存是否紧张,不自动清理任何缓存,保留图片原始品质
|
||||||
*/
|
*/
|
||||||
public class BackgroundView extends RelativeLayout {
|
public class BackgroundView extends RelativeLayout {
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ public class BackgroundView extends RelativeLayout {
|
|||||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
);
|
);
|
||||||
mIvBackground.setLayoutParams(ivParams);
|
mIvBackground.setLayoutParams(ivParams);
|
||||||
mIvBackground.setScaleType(ScaleType.FIT_CENTER); // 保持比例+居中平铺
|
mIvBackground.setScaleType(ScaleType.FIT_CENTER); // 保持比例+居中平铺(无拉伸裁剪)
|
||||||
mIvBackground.setBackgroundColor(0x00000000);
|
mIvBackground.setBackgroundColor(0x00000000);
|
||||||
mLlContainer.addView(mIvBackground);
|
mLlContainer.addView(mIvBackground);
|
||||||
LogUtils.d(TAG, "=== initImageView 完成 ===");
|
LogUtils.d(TAG, "=== initImageView 完成 ===");
|
||||||
@@ -124,11 +124,11 @@ public class BackgroundView extends RelativeLayout {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 核心修改:刷新时不删除旧缓存,仅重新解码并更新缓存(强制保持策略)
|
// 核心修改:刷新时不删除旧缓存,仅重新解码原始品质图片并更新缓存(强制保持策略)
|
||||||
if (isRefresh) {
|
if (isRefresh) {
|
||||||
LogUtils.d(TAG, "loadByBackgroundBean: 刷新图片,重新解码并更新缓存 - " + targetPath);
|
LogUtils.d(TAG, "loadByBackgroundBean: 刷新图片,重新解码原始品质图片并更新缓存 - " + targetPath);
|
||||||
// 刷新时直接解码,更新缓存(不删除旧缓存)
|
// 刷新时直接解码原始品质图片,更新缓存(不删除旧缓存)
|
||||||
Bitmap newBitmap = decodeBitmapWithCompress(new File(targetPath), 1080, 1920);
|
Bitmap newBitmap = decodeOriginalBitmap(new File(targetPath));
|
||||||
if (newBitmap != null) {
|
if (newBitmap != null) {
|
||||||
App.sBitmapCacheUtils.cacheBitmap(targetPath, newBitmap);
|
App.sBitmapCacheUtils.cacheBitmap(targetPath, newBitmap);
|
||||||
// 增加引用计数
|
// 增加引用计数
|
||||||
@@ -140,7 +140,7 @@ public class BackgroundView extends RelativeLayout {
|
|||||||
|
|
||||||
// ====================================== 对外方法 ======================================
|
// ====================================== 对外方法 ======================================
|
||||||
/**
|
/**
|
||||||
* 改进版:强制保持缓存策略,不自动清理任何缓存,强化引用计数管理
|
* 改进版:强制保持缓存策略,不自动清理任何缓存,强化引用计数管理,保留图片原始品质
|
||||||
* @param imagePath 图片绝对路径
|
* @param imagePath 图片绝对路径
|
||||||
*/
|
*/
|
||||||
public void loadImage(String imagePath) {
|
public void loadImage(String imagePath) {
|
||||||
@@ -164,14 +164,14 @@ public class BackgroundView extends RelativeLayout {
|
|||||||
if (imagePath.equals(mCurrentCachedPath)) {
|
if (imagePath.equals(mCurrentCachedPath)) {
|
||||||
Bitmap cachedBitmap = App.sBitmapCacheUtils.getCachedBitmap(imagePath);
|
Bitmap cachedBitmap = App.sBitmapCacheUtils.getCachedBitmap(imagePath);
|
||||||
if (isBitmapValid(cachedBitmap)) {
|
if (isBitmapValid(cachedBitmap)) {
|
||||||
LogUtils.d(TAG, "loadImage: 路径未变,使用有效缓存 Bitmap");
|
LogUtils.d(TAG, "loadImage: 路径未变,使用有效缓存 Bitmap(原始品质)");
|
||||||
mImageAspectRatio = (float) cachedBitmap.getWidth() / cachedBitmap.getHeight();
|
mImageAspectRatio = (float) cachedBitmap.getWidth() / cachedBitmap.getHeight();
|
||||||
mIvBackground.setImageBitmap(cachedBitmap);
|
mIvBackground.setImageBitmap(cachedBitmap);
|
||||||
adjustImageViewSize();
|
adjustImageViewSize();
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e(TAG, "loadImage: 缓存Bitmap无效,尝试重加载 - " + imagePath);
|
LogUtils.e(TAG, "loadImage: 缓存Bitmap无效,尝试重加载原始品质图片 - " + imagePath);
|
||||||
// 缓存无效,直接重加载(不删除旧缓存,强制保持策略)
|
// 缓存无效,直接重加载原始品质图片(不删除旧缓存,强制保持策略)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +182,7 @@ public class BackgroundView extends RelativeLayout {
|
|||||||
}
|
}
|
||||||
// ======================== 路径判断逻辑结束 ========================
|
// ======================== 路径判断逻辑结束 ========================
|
||||||
|
|
||||||
// 无缓存/缓存无效/路径更新:重新加载图片
|
// 无缓存/缓存无效/路径更新:重新加载原始品质图片
|
||||||
if (!calculateImageAspectRatio(imageFile)) {
|
if (!calculateImageAspectRatio(imageFile)) {
|
||||||
setDefaultTransparentBackground();
|
setDefaultTransparentBackground();
|
||||||
return;
|
return;
|
||||||
@@ -191,19 +191,19 @@ public class BackgroundView extends RelativeLayout {
|
|||||||
// 先尝试从缓存获取
|
// 先尝试从缓存获取
|
||||||
Bitmap bitmap = App.sBitmapCacheUtils.getCachedBitmap(imagePath);
|
Bitmap bitmap = App.sBitmapCacheUtils.getCachedBitmap(imagePath);
|
||||||
if (isBitmapValid(bitmap)) {
|
if (isBitmapValid(bitmap)) {
|
||||||
LogUtils.d(TAG, "loadImage: 从缓存获取有效Bitmap - " + imagePath);
|
LogUtils.d(TAG, "loadImage: 从缓存获取有效Bitmap(原始品质) - " + imagePath);
|
||||||
} else {
|
} else {
|
||||||
// 缓存无效应解码
|
// 缓存无效应解码原始品质图片
|
||||||
bitmap = decodeBitmapWithCompress(imageFile, 1080, 1920);
|
bitmap = decodeOriginalBitmap(imageFile);
|
||||||
if (bitmap == null) {
|
if (bitmap == null) {
|
||||||
LogUtils.e(TAG, "loadImage: 图片解码失败");
|
LogUtils.e(TAG, "loadImage: 图片解码失败(原始品质)");
|
||||||
ToastUtils.show("图片解码失败,无法加载");
|
ToastUtils.show("图片解码失败,无法加载");
|
||||||
setDefaultTransparentBackground();
|
setDefaultTransparentBackground();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 缓存新图片(强制保持)
|
// 缓存新图片(强制保持,原始品质)
|
||||||
App.sBitmapCacheUtils.cacheBitmap(imagePath, bitmap);
|
App.sBitmapCacheUtils.cacheBitmap(imagePath, bitmap);
|
||||||
LogUtils.d(TAG, "loadImage: 加载新图片并缓存 - " + imagePath);
|
LogUtils.d(TAG, "loadImage: 加载新图片并缓存(原始品质) - " + imagePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 增加引用计数(配合全局缓存工具的引用计数机制)
|
// 增加引用计数(配合全局缓存工具的引用计数机制)
|
||||||
@@ -248,29 +248,21 @@ public class BackgroundView extends RelativeLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 改进版解码方法:强制缓存策略,关闭可回收标志,使用高压缩比减少OOM风险
|
* 移除压缩逻辑:解码原始品质图片(无缩放、无色彩损失)
|
||||||
*/
|
*/
|
||||||
private Bitmap decodeBitmapWithCompress(File file, int maxWidth, int maxHeight) {
|
private Bitmap decodeOriginalBitmap(File file) {
|
||||||
try {
|
try {
|
||||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
options.inJustDecodeBounds = true;
|
// 核心配置:无缩放、全彩品质、关闭可回收标志(强制保持)
|
||||||
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
options.inSampleSize = 1; // 不缩放,采样率为1
|
||||||
|
options.inPreferredConfig = Bitmap.Config.ARGB_8888; // 全彩品质,无色彩损失
|
||||||
// 更精准的采样率计算(避免过度压缩)
|
options.inPurgeable = false; // 关闭可回收标志,防止系统主动回收
|
||||||
int scaleX = (int) Math.ceil((float) options.outWidth / maxWidth);
|
|
||||||
int scaleY = (int) Math.ceil((float) options.outHeight / maxHeight);
|
|
||||||
int inSampleSize = Math.max(scaleX, scaleY);
|
|
||||||
inSampleSize = Math.max(1, inSampleSize); // 确保采样率≥1
|
|
||||||
|
|
||||||
options.inJustDecodeBounds = false;
|
|
||||||
options.inSampleSize = inSampleSize;
|
|
||||||
options.inPreferredConfig = Bitmap.Config.RGB_565; // 节省内存
|
|
||||||
// 核心修改:关闭可回收标志,防止系统主动回收(强制缓存策略)
|
|
||||||
options.inPurgeable = false;
|
|
||||||
options.inInputShareable = false;
|
options.inInputShareable = false;
|
||||||
|
options.inDither = true; // 开启抖动,保证色彩还原精度
|
||||||
|
options.inScaled = false; // 关闭自动缩放,保留原始尺寸
|
||||||
return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LogUtils.e(TAG, "压缩解码失败:" + e.getMessage());
|
LogUtils.e(TAG, "原始品质解码失败:" + e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -308,7 +300,7 @@ public class BackgroundView extends RelativeLayout {
|
|||||||
params.width = ivWidth;
|
params.width = ivWidth;
|
||||||
params.height = ivHeight;
|
params.height = ivHeight;
|
||||||
mIvBackground.setLayoutParams(params);
|
mIvBackground.setLayoutParams(params);
|
||||||
mIvBackground.setScaleType(ScaleType.FIT_CENTER);
|
mIvBackground.setScaleType(ScaleType.FIT_CENTER); // 仅居中平铺,无缩放
|
||||||
mIvBackground.setVisibility(View.VISIBLE);
|
mIvBackground.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,7 +352,7 @@ public class BackgroundView extends RelativeLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重写:恢复尺寸调整逻辑,确保View尺寸变化时正确显示
|
* 重写:恢复尺寸调整逻辑,确保View尺寸变化时正确显示(无压缩)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
|
|||||||
Reference in New Issue
Block a user