diff --git a/powerbell/build.properties b/powerbell/build.properties index 2d6784f..477b97b 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Thu Dec 11 10:46:09 GMT 2025 +#Thu Dec 11 11:14:29 GMT 2025 stageCount=15 libraryProject= baseVersion=15.12 publishVersion=15.12.14 -buildCount=26 +buildCount=28 baseBetaVersion=15.12.15 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 ea373f7..0b6d024 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 @@ -50,7 +50,7 @@ public class BitmapCacheUtils { /** * 核心接口:根据图片路径缓存 Bitmap 到内存 * @param imagePath 图片绝对路径 - * @return 缓存成功的 Bitmap / null(路径无效/文件不存在) + * @return 缓存成功的 Bitmap / null(路径无效/文件不存在/解码失败) */ public Bitmap cacheBitmap(String imagePath) { if (TextUtils.isEmpty(imagePath)) { @@ -59,15 +59,23 @@ public class BitmapCacheUtils { } File imageFile = new File(imagePath); - if (!imageFile.exists() || !imageFile.isFile()) { - LogUtils.e(TAG, "cacheBitmap: 图片文件不存在 - " + imagePath); + if (!imageFile.exists() || !imageFile.isFile() || imageFile.length() <= 0) { + LogUtils.e(TAG, "cacheBitmap: 图片文件无效(不存在/非文件/空文件) - " + imagePath); return null; } // 已缓存则直接返回,避免重复加载 if (mBitmapCacheMap.containsKey(imagePath)) { - LogUtils.d(TAG, "cacheBitmap: 图片已缓存,直接返回 - " + imagePath); - return mBitmapCacheMap.get(imagePath); + Bitmap cachedBitmap = mBitmapCacheMap.get(imagePath); + // 额外校验缓存的Bitmap是否有效 + if (cachedBitmap != null && !cachedBitmap.isRecycled()) { + LogUtils.d(TAG, "cacheBitmap: 图片已缓存,直接返回 - " + imagePath); + return cachedBitmap; + } else { + // 缓存的Bitmap已失效,移除后重新加载 + mBitmapCacheMap.remove(imagePath); + LogUtils.w(TAG, "cacheBitmap: 缓存Bitmap已失效,移除后重新加载 - " + imagePath); + } } // 压缩加载 Bitmap(避免OOM) @@ -85,25 +93,33 @@ public class BitmapCacheUtils { /** * 核心接口:根据路径获取缓存的 Bitmap * @param imagePath 图片绝对路径 - * @return 缓存的 Bitmap / null + * @return 缓存的有效 Bitmap / null(未缓存/已回收) */ public Bitmap getCachedBitmap(String imagePath) { if (TextUtils.isEmpty(imagePath)) { return null; } - return mBitmapCacheMap.get(imagePath); + Bitmap bitmap = mBitmapCacheMap.get(imagePath); + // 校验Bitmap是否有效 + if (bitmap != null && bitmap.isRecycled()) { + mBitmapCacheMap.remove(imagePath); + return null; + } + return bitmap; } /** * 清空所有 Bitmap 缓存(释放内存) */ public void clearAllCache() { - for (Bitmap bitmap : mBitmapCacheMap.values()) { - if (bitmap != null && !bitmap.isRecycled()) { - bitmap.recycle(); // 主动回收 Bitmap + synchronized (mBitmapCacheMap) { + for (Bitmap bitmap : mBitmapCacheMap.values()) { + if (bitmap != null && !bitmap.isRecycled()) { + bitmap.recycle(); // 主动回收 Bitmap + } } + mBitmapCacheMap.clear(); } - mBitmapCacheMap.clear(); LogUtils.d(TAG, "clearAllCache: 所有 Bitmap 缓存已清空"); } @@ -115,22 +131,39 @@ public class BitmapCacheUtils { if (TextUtils.isEmpty(imagePath)) { return; } - Bitmap bitmap = mBitmapCacheMap.remove(imagePath); - if (bitmap != null && !bitmap.isRecycled()) { - bitmap.recycle(); - LogUtils.d(TAG, "removeCachedBitmap: 移除并回收缓存 - " + imagePath); + synchronized (mBitmapCacheMap) { + Bitmap bitmap = mBitmapCacheMap.remove(imagePath); + if (bitmap != null && !bitmap.isRecycled()) { + bitmap.recycle(); + LogUtils.d(TAG, "removeCachedBitmap: 移除并回收缓存 - " + imagePath); + } } } /** * 压缩解码 Bitmap(按最大尺寸缩放,避免OOM) + * @param imagePath 图片绝对路径 + * @return 解码后的 Bitmap / null(文件无效/解码失败) */ private Bitmap decodeCompressedBitmap(String imagePath) { + // 前置校验:确保文件有效 + File imageFile = new File(imagePath); + if (!imageFile.exists() || !imageFile.isFile() || imageFile.length() <= 0) { + LogUtils.e(TAG, "decodeCompressedBitmap: 文件无效,跳过解码 - " + imagePath); + return null; + } + BitmapFactory.Options options = new BitmapFactory.Options(); // 第一步:只获取图片尺寸,不加载像素 options.inJustDecodeBounds = true; BitmapFactory.decodeFile(imagePath, options); + // 校验尺寸是否有效 + if (options.outWidth <= 0 || options.outHeight <= 0) { + LogUtils.e(TAG, "decodeCompressedBitmap: 图片尺寸无效 - " + imagePath); + return null; + } + // 计算缩放比例 int sampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT); @@ -141,7 +174,15 @@ public class BitmapCacheUtils { options.inPurgeable = true; options.inInputShareable = true; - return BitmapFactory.decodeFile(imagePath, options); + try { + return BitmapFactory.decodeFile(imagePath, options); + } catch (OutOfMemoryError e) { + LogUtils.e(TAG, "decodeCompressedBitmap: OOM异常 - " + imagePath); + return null; + } catch (Exception e) { + LogUtils.e(TAG, "decodeCompressedBitmap: 解码异常 - " + imagePath, e); + return null; + } } /** 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 27d6e8e..39a14c8 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 @@ -117,7 +117,7 @@ public class BackgroundView extends RelativeLayout { // ====================================== 对外方法 ====================================== /** - * 改造后:添加路径判断,路径更新时同步更新缓存 + * 改造后:添加路径判断,路径更新时同步更新缓存;缓存Bitmap为null时提示并加载透明背景 * @param imagePath 图片绝对路径 */ public void loadImage(String imagePath) { @@ -139,8 +139,8 @@ public class BackgroundView extends RelativeLayout { // ======================== 新增:路径判断逻辑 ======================== // 1. 路径未变化:直接使用缓存 if (imagePath.equals(mCurrentCachedPath)) { - //ToastUtils.show("isReload == false"); Bitmap cachedBitmap = App._mBitmapCacheUtils.getCachedBitmap(imagePath); + // 核心修改:判断缓存Bitmap是否为null if (cachedBitmap != null && !cachedBitmap.isRecycled()) { LogUtils.d(TAG, "loadImage: 路径未变,使用缓存 Bitmap"); mImageAspectRatio = (float) cachedBitmap.getWidth() / cachedBitmap.getHeight(); @@ -148,7 +148,13 @@ public class BackgroundView extends RelativeLayout { adjustImageViewSize(); mIvBackground.setVisibility(View.VISIBLE); return; - } + } else { + // 缓存Bitmap为空或已回收,提示并加载透明背景 + LogUtils.e(TAG, "loadImage: 全局位图缓存为空或已回收 - " + imagePath); + ToastUtils.show("全局位图缓存为空,无法加载图片"); + setDefaultTransparentBackground(); + return; + } } // 2. 路径已更新:移除旧缓存,加载新图片并更新缓存 @@ -166,6 +172,8 @@ public class BackgroundView extends RelativeLayout { Bitmap bitmap = decodeBitmapWithCompress(imageFile, 1080, 1920); if (bitmap == null) { + LogUtils.e(TAG, "loadImage: 图片解码失败"); + ToastUtils.show("图片解码失败,无法加载"); setDefaultTransparentBackground(); return; } @@ -258,6 +266,7 @@ public class BackgroundView extends RelativeLayout { mImageAspectRatio = 1.0f; // 清空缓存路径记录 mCurrentCachedPath = ""; + mIvBackground.setVisibility(View.GONE); } // ====================================== 重写方法 ======================================