/* * Copyright (C) 2016 Peng fei Pan <sky@xiaopan.me> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.xiaopan.sketch.cache; import android.annotation.TargetApi; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Rect; import android.os.Build; import android.text.TextUtils; import me.xiaopan.sketch.SLog; import me.xiaopan.sketch.SLogType; import me.xiaopan.sketch.SketchMonitor; import me.xiaopan.sketch.decode.ImageType; import me.xiaopan.sketch.util.SketchUtils; public class BitmapPoolUtils { /** * SDK版本是否支持inBitmap,适用于BitmapFactory */ public static boolean sdkSupportInBitmap() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; } /** * SDK版本是否支持inBitmap,适用于BitmapRegionDecoder */ public static boolean sdkSupportInBitmapForRegionDecoder() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; } /** * 从bitmap poo中取出可复用的Bitmap设置到inBitmap上,适用于BitmapFactory * * @param options BitmapFactory.Options 需要用到options的outWidth、outHeight、inSampleSize以及inPreferredConfig属性 * @param bitmapPool BitmapPool 从这个池子里找可复用的Bitmap * @return true:找到了可复用的Bitmap */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) public static boolean setInBitmapFromPool(BitmapFactory.Options options, BitmapPool bitmapPool) { if (!sdkSupportInBitmap()) { return false; } if (options.outWidth == 0 || options.outHeight == 0) { SLog.e(SLogType.REQUEST, "outWidth or ourHeight is 0"); return false; } if (TextUtils.isEmpty(options.outMimeType)) { SLog.e(SLogType.REQUEST, "outMimeType is empty"); return false; } // 使用inBitmap时4.4以下inSampleSize不能为0,最小也得是1 if (options.inSampleSize <= 0) { options.inSampleSize = 1; } int inSampleSize = options.inSampleSize; ImageType imageType = ImageType.valueOfMimeType(options.outMimeType); Bitmap inBitmap = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { int finalWidth = SketchUtils.ceil(options.outWidth, inSampleSize); int finalHeight = SketchUtils.ceil(options.outHeight, inSampleSize); inBitmap = bitmapPool.get(finalWidth, finalHeight, options.inPreferredConfig); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && inSampleSize == 1 && (imageType == ImageType.JPEG || imageType == ImageType.PNG)) { inBitmap = bitmapPool.get(options.outWidth, options.outHeight, options.inPreferredConfig); } if (inBitmap != null && SLogType.CACHE.isEnabled()) { int sizeInBytes = SketchUtils.computeByteCount(options.outWidth, options.outHeight, options.inPreferredConfig); SLog.d(SLogType.CACHE, "setInBitmapFromPool. options=%dx%d,%s,%d,%d. inBitmap=%s,%d", options.outWidth, options.outHeight, options.inPreferredConfig, inSampleSize, sizeInBytes, Integer.toHexString(inBitmap.hashCode()), SketchUtils.getByteCount(inBitmap)); } options.inBitmap = inBitmap; options.inMutable = true; return inBitmap != null; } public static boolean inBitmapThrow(Throwable throwable, BitmapFactory.Options options, SketchMonitor monitor, BitmapPool bitmapPool, String imageUri, int imageWidth, int imageHeight) { if (throwable instanceof IllegalArgumentException) { if (sdkSupportInBitmap()) { if (options.inBitmap != null) { monitor.onInBitmapException(imageUri, imageWidth, imageHeight, options.inSampleSize, options.inBitmap); freeBitmapToPool(options.inBitmap, bitmapPool); return true; } } } return false; } /** * 处理bitmap,首先尝试放入bitmap pool,放不进去就回收 * * @param bitmap 要处理的bitmap * @param bitmapPool BitmapPool 尝试放入这个池子 * @return ture:成功放入bitmap pool */ public static boolean freeBitmapToPool(Bitmap bitmap, BitmapPool bitmapPool) { if (bitmap == null || bitmap.isRecycled()) { return false; } boolean success = bitmapPool.put(bitmap); if (success) { if (SLogType.CACHE.isEnabled()) { StackTraceElement[] elements = new Exception().getStackTrace(); StackTraceElement element = elements.length > 1 ? elements[1] : elements[0]; SLog.d(SLogType.CACHE, String.format("Put to bitmap pool. info:%dx%d,%s,%s - %s.%s:%d", bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig(), SketchUtils.toHexString(bitmap), element.getClassName(), element.getMethodName(), element.getLineNumber())); } } else { if (SLogType.CACHE.isEnabled()) { StackTraceElement[] elements = new Exception().getStackTrace(); StackTraceElement element = elements.length > 1 ? elements[1] : elements[0]; SLog.w(SLogType.CACHE, String.format("Recycle bitmap. info:%dx%d,%s,%s - %s.%s:%d", bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig(), SketchUtils.toHexString(bitmap), element.getClassName(), element.getMethodName(), element.getLineNumber())); } bitmap.recycle(); } return success; } /** * 从bitmap poo中取出可复用的Bitmap设置到inBitmap上,适用于BitmapRegionDecoder * * @param options BitmapFactory.Options 需要用到options的outWidth、outHeight、inSampleSize以及inPreferredConfig属性 * @param bitmapPool BitmapPool 从这个池子里找可复用的Bitmap * @return true:找到了可复用的Bitmap */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public static boolean setInBitmapFromPoolForRegionDecoder(BitmapFactory.Options options, Rect srcRect, BitmapPool bitmapPool) { if (!sdkSupportInBitmapForRegionDecoder()) { return false; } int inSampleSize = options.inSampleSize >= 1 ? options.inSampleSize : 1; Bitmap.Config config = options.inPreferredConfig; int finalWidth = SketchUtils.ceil(srcRect.width(), inSampleSize); int finalHeight = SketchUtils.ceil(srcRect.height(), inSampleSize); Bitmap inBitmap = bitmapPool.get(finalWidth, finalHeight, config); if (inBitmap != null) { if (SLogType.CACHE.isEnabled()) { int sizeInBytes = SketchUtils.computeByteCount(finalWidth, finalHeight, config); SLog.d(SLogType.CACHE, "setInBitmapFromPoolForRegionDecoder. options=%dx%d,%s,%d,%d. inBitmap=%s,%d", finalWidth, finalHeight, config, inSampleSize, sizeInBytes, Integer.toHexString(inBitmap.hashCode()), SketchUtils.getByteCount(inBitmap)); } } else { // 由于BitmapRegionDecoder不支持inMutable所以就自己创建Bitmap inBitmap = Bitmap.createBitmap(finalWidth, finalHeight, config); } options.inBitmap = inBitmap; return inBitmap != null; } public static boolean inBitmapThrowForRegionDecoder(Throwable throwable, BitmapFactory.Options options, SketchMonitor monitor, BitmapPool bitmapPool, String imageUri, int imageWidth, int imageHeight, Rect srcRect) { if (throwable instanceof IllegalArgumentException) { if (sdkSupportInBitmapForRegionDecoder()) { if (options.inBitmap != null) { monitor.onInBitmapExceptionForRegionDecoder(imageUri, imageWidth, imageHeight, srcRect, options.inSampleSize, options.inBitmap); freeBitmapToPoolForRegionDecoder(options.inBitmap, bitmapPool); return true; } } } return false; } /** * 处理bitmap,首先尝试放入bitmap pool,放不进去就回收 * * @param bitmap 要处理的bitmap * @param bitmapPool BitmapPool 尝试放入这个池子 * @return true:成功放入bitmap pool */ public static boolean freeBitmapToPoolForRegionDecoder(Bitmap bitmap, BitmapPool bitmapPool) { if (bitmap == null || bitmap.isRecycled()) { return false; } boolean success = sdkSupportInBitmapForRegionDecoder() && bitmapPool.put(bitmap); if (!success) { if (SLogType.CACHE.isEnabled()) { StackTraceElement[] elements = new Exception().getStackTrace(); StackTraceElement element = elements.length > 1 ? elements[1] : elements[0]; SLog.w(SLogType.CACHE, String.format("Recycle bitmap. info:%dx%d,%s,%s - %s.%s:%d", bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig(), SketchUtils.toHexString(bitmap), element.getClassName(), element.getMethodName(), element.getLineNumber())); } bitmap.recycle(); } else { if (SLogType.CACHE.isEnabled()) { StackTraceElement[] elements = new Exception().getStackTrace(); StackTraceElement element = elements.length > 1 ? elements[1] : elements[0]; SLog.d(SLogType.CACHE, String.format("Put to bitmap pool. info:%dx%d,%s,%s - %s.%s:%d", bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig(), SketchUtils.toHexString(bitmap), element.getClassName(), element.getMethodName(), element.getLineNumber())); } } return success; } }