一、BitmapFactory 设置优化
1.inJustDecodeBounds
BitmapFactory ,它提供了4类方法,分别是:
decodeResource、 从资源加载出Bitmap对象
decodeStream、从字节数组加载出Bitmap对象
decodeFile 从文件加载出Bitmap对象
decodeByteArray 从字节数组加载出Bitmap对象
这些方法都会为Bitmap分配内存,那就有可能发生OOM,想象一下一张分辨率超高的图片加载进内存 了!那有什么办法可以避免呢。这时候BitmapFactory.Options就要上场了,将它的属性
inJustDecodeBounds设置为true就可以让解析方法不给Bitmap分配内存,也就能防止OOM,返回值也
不是实际的bitmap,而是null,但是我们还是可以查询图片的相关信息比如宽、高。
bmOptions.inJustDecodeBounds = true;
这个参数设置为true 就不会
2.inSampleSize
BitmapFactory
提供了 inSampleSize
参数,可以通过它来控制加载的图像的分辨率,从而减少内存消耗。通过计算适当的 inSampleSize
值,你可以在加载图像时缩小图像的尺寸。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 只解码边界信息,不加载实际的图像
BitmapFactory.decodeFile(filePath, options);
// 计算 inSampleSize
int scaleFactor = 1;
if (options.outHeight > targetHeight || options.outWidth > targetWidth) {
final int halfHeight = options.outHeight / 2;
final int halfWidth = options.outWidth / 2;
while ((halfHeight / scaleFactor) > targetHeight && (halfWidth / scaleFactor) > targetWidth) {
scaleFactor *= 2;
}
}
options.inSampleSize = scaleFactor;
options.inJustDecodeBounds = false; // 重新加载图像
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
3.inBitmap
BitmapFactory.Options.inBitmap
是一个用于复用内存的关键字段,它允许你指定一个已经存在的 Bitmap
对象,BitmapFactory 会尝试将其作为新的解码目标,而不是为每张图片重新分配内存。
// 1. 创建一个可复用的 Bitmap 对象
Bitmap reusableBitmap = Bitmap.createBitmap(800, 600, Bitmap.Config.ARGB_8888);
// 2. 设置 BitmapFactory.Options.inBitmap 为可复用的 Bitmap
BitmapFactory.Options options = new BitmapFactory.Options();
options.inBitmap = reusableBitmap; // 设置复用内存
options.inJustDecodeBounds = true; // 只解码边界信息,不加载图像
// 3. 读取图片的边界信息
BitmapFactory.decodeFile(filePath, options);
// 4. 根据图片的尺寸计算合适的 inSampleSize
options.inJustDecodeBounds = false; // 重新设置为解码实际的图像
options.inSampleSize = 2; // 通过 inSampleSize 控制采样率,减小内存使用
// 5. 解码图片并复用内存
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
// 6. 使用该 bitmap 进行显示或其他操作
imageView.setImageBitmap(bitmap);
二、区域解码
假设我们有一张非常大的照片,例如它的分辨率是4000 * 3000。
那么,在常规的的手机屏幕(1080 * 1920)上,如果不做压缩处理,我们的图片很明显是显示不开 的。因此,我们想到了这样的方案:让图片支持滑动,滑动到哪里,加载哪一部分。如下图片,我们要 在手机上按照1:1的比例高清展示出来,那么1080*1920的区域,大概只能让他显示黑框中的区域。那其 他的区域就需要我们滑动去加载。滑动到哪里,就展示哪一块区域。
// 1. 获取输入流(比如从文件或资源)
FileInputStream fileInputStream = new FileInputStream(imagePath);
// 2. 创建 BitmapRegionDecoder
BitmapRegionDecoder regionDecoder = BitmapRegionDecoder.newInstance(fileInputStream, false);
// 3. 定义要解码的区域(指定一个矩形区域)
Rect rect = new Rect(0, 0, 500, 500); // 这里以 (0, 0) 到 (500, 500) 的区域为例
// 4. 解码指定区域的图片
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2; // 通过 inSampleSize 控制采样率,减小内存使用
Bitmap bitmap = regionDecoder.decodeRegion(rect, options);
// 5. 使用该 bitmap,例如显示在 ImageView 中
imageView.setImageBitmap(bitmap);
内存复用的好处
- Bitmap 大小限制: 复用内存的 Bitmap 对象必须与解码后的 Bitmap 在内存尺寸上兼容。即,如果目标图像比复用的
Bitmap
大,则会抛出IllegalArgumentException
。因此,通常需要根据图片的实际尺寸计算合适的内存复用对象。 - 内存复用的条件: 使用
inBitmap
时,如果提供的复用Bitmap
的尺寸不适应新的图片,系统会抛出IllegalArgumentException
。这就意味着你需要确保复用的Bitmap
对象的大小足够大,或者计算适当的图像解码比例。 - 减少垃圾回收压力: 在使用
inBitmap
进行内存复用时,由于不需要重新分配内存,会减少频繁的垃圾回收,从而提高性能。 - 释放复用的 Bitmap: 使用完复用的
Bitmap
后,仍然需要手动回收它。调用recycle()
方法可以释放Bitmap
占用的内存。否则,Bitmap
会继续占用内存,直到系统回收。 - 必须使用相同的配置: 复用内存的
Bitmap
必须具有相同的颜色配置和像素格式,否则可能导致图像解码失败。例如,你不能将Bitmap.Config.ARGB_8888
的Bitmap
用于Bitmap.Config.RGB_565
类型的图像
三、使用硬件加速
如果设备支持硬件加速,可以启用硬件加速来提高图片渲染性能。硬件加速可以让图像绘制过程更加高效,减少 CPU 的负担。
<application
android:hardwareAccelerated="true">
四、Bitmap 回收处理
如果你不再需要某个 Bitmap 对象,可以调用 recycle()
方法来手动回收它占用的内存。
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null;
}
如果不适用recycle() 那么bitmap的释放不一定及时,要等 垃圾回收才会被清理掉,使用recycle 会释放掉 bitmap的内存
五、Glide Picasso 优化
1.适时的裁剪图片
Glide.with(context)
.load(imageUrl)
.override(targetWidth, targetHeight) // 设置目标尺寸
.centerCrop() // 裁剪图片以适应目标尺寸
.into(imageView);
Picasso.get()
.load(imageUrl)
.resize(targetWidth, targetHeight) // 设置目标尺寸
.centerCrop() // 裁剪图片以适应目标尺寸
.into(imageView);
2.使用内存缓存
Glide.with(context)
.load(imageUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL) // 缓存所有图像版本(原始、压缩图像等)
.skipMemoryCache(false) // 启用内存缓存
.into(imageView);
Picasso.get()
.load(imageUrl)
.memoryPolicy(MemoryPolicy.NO_CACHE) // 禁用内存缓存(如果需要)
.networkPolicy(NetworkPolicy.NO_CACHE) // 禁用网络缓存
.into(imageView);
六、图片压缩处理
1.compress 压缩
可以使用 Bitmap.compress()
方法,将图片压缩成指定质量的 JPEG 格式,从而减少内存消耗。
public void compressBitmap(Bitmap bitmap, File outputFile, int quality) throws IOException {
FileOutputStream fos = new FileOutputStream(outputFile);
// 有损压缩,质量范围是 0(最差)到 100(最佳)
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, fos);
fos.close();
}
- 压缩质量:
quality
参数控制压缩后的质量,0 表示最差质量,100 表示最优质量。 - 常见格式:
Bitmap.CompressFormat.JPEG
(有损压缩)和Bitmap.CompressFormat.PNG
(无损压缩)。 - 文件大小与质量的平衡:一般来说,压缩质量设置为 80-90 左右可以获得比较好的平衡,既能够减少文件大小,又能保证图像质量。
2.luban压缩
目前比较好用的压缩第三方