Bitmap
参考:高效加载大型位图
参考:管理位图内存
不同 Android 版本时的Bitmap内存模型
API 级别 |
API 10- |
API 11 ~ API 25 |
API 26+ |
Bitmap 对象存放 |
Java heap |
Java heap |
Java heap |
像素 (pixel data)数据存放 |
native heap |
Java heap |
native heap |
API 10-
该方式有个缺点,就是 java
层已经被回收掉了,但是 native
层并不清楚,回收时机不明确,所以在 API 11 ~ API 25
将它放入到 java
层中,在 API 26+
又做了优化,java
回收后能迅速通知到到 native
层
获取 Bitmap 占用内存
getByteCount
函数:直接返回 bitmap
占用的内存,但需要运行时动态算出来
缩小图片
方案一:
该方案得到的结果宽高比例并不一定与 inSampleSize
一致,加载完后还会根据密度等比放大或缩小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| val targetSize = 400 val options = BitmapFactory.Options() options.inJustDecodeBounds = true BitmapFactory.decodeResource(resources, R.mipmap.avatar, options) options.inJustDecodeBounds = false val (height: Int, width: Int) = options.run { outHeight to outWidth } var inSampleSize = 1 if (height > targetSize || width > targetSize) { val halfHeight: Int = height / 2 val halfWidth: Int = width / 2 while (halfHeight / inSampleSize >= targetSize && halfWidth / inSampleSize >= targetSize) { inSampleSize *= 2 } } options.inSampleSize = inSampleSize val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.avatar, options)
|
方案二:
该方案会将图片按 targetSize/outWidth
的比例进行缩放
1 2 3 4 5 6 7 8 9
| val targetSize = 400 val options = BitmapFactory.Options() options.inJustDecodeBounds = true BitmapFactory.decodeResource(resources, R.mipmap.avatar, options) options.inJustDecodeBounds = false options.inScaled = true options.inDensity = options.outWidth options.inTargetDensity = targetSize val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.avatar, options)
|
【注意】上述两种方案得到的Bitmap密度并不一致
在 Android 3.0 及更高版本上管理内存
声明重用管理类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| public class ImageCache {
private final Set<SoftReference<Bitmap>> mReusableBitmaps = Collections.synchronizedSet(new HashSet<>()); private final LruCache<String, BitmapDrawable> mMemoryCache = new LruCache<String, BitmapDrawable>(1024) { @Override protected void entryRemoved(boolean evicted, @NotNull String key, @NotNull BitmapDrawable oldValue, BitmapDrawable newValue) { Bitmap bitmap = oldValue.getBitmap(); if (bitmap.isMutable()) { mReusableBitmaps.add(new SoftReference<>(bitmap)); } } };
protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) { Bitmap bitmap = null; if (!mReusableBitmaps.isEmpty()) { synchronized (mReusableBitmaps) { final Iterator<SoftReference<Bitmap>> iterator = mReusableBitmaps.iterator(); Bitmap item; while (iterator.hasNext()) { item = iterator.next().get(); if (item != null && item.isMutable()) { if (canUseForInBitmap(item, options)) { bitmap = item; iterator.remove(); break; } } else { iterator.remove(); } } } } return bitmap; }
public static boolean canUseForInBitmap(Bitmap candidate, BitmapFactory.Options targetOptions) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { int width = targetOptions.outWidth / targetOptions.inSampleSize; int height = targetOptions.outHeight / targetOptions.inSampleSize; int byteCount = width * height * getBytesPerPixel(candidate.getConfig()); return byteCount <= candidate.getAllocationByteCount(); } return candidate.getWidth() == targetOptions.outWidth && candidate.getHeight() == targetOptions.outHeight && targetOptions.inSampleSize == 1; }
public static int getBytesPerPixel(Bitmap.Config config) { if (config == Bitmap.Config.ARGB_8888) { return 4; } else if (config == Bitmap.Config.RGB_565) { return 2; } else if (config == Bitmap.Config.ARGB_4444) { return 2; } else if (config == Bitmap.Config.ALPHA_8) { return 1; } return 1; } }
|
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class BitmapUtil {
public static Bitmap decodeSampledBitmapFromFile(String filename, int reqWidth, int reqHeight, ImageCache cache) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(filename, options); addInBitmapOptions(options, cache); options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(filename, options); }
private static void addInBitmapOptions(BitmapFactory.Options options, ImageCache cache) { if (cache != null) { Bitmap inBitmap = cache.getBitmapFromReusableSet(options); if (inBitmap != null) { options.inMutable = true; options.inBitmap = inBitmap; } } } }
|