Android 性能优化之Loading Big Bitmaps
来源:互联网 发布:网络大电影收入计算 编辑:程序博客网 时间:2024/06/07 13:19
高效加载Large Bitmaps
加载大Bitmaps到内存中,总是会有各种各样的问题,我们在开发过程中,经常会遇到因为图片资源过大导致OOM。我们应该始终留意在Android中每一个应用占用的内存大小是有上限的,过了这个上限,系统就回报OOM,用户体验非常差。
今天我们就聊一聊如何加载Large Bitmaps,了解以下它具体是如何工作的。
这篇文章只是用来聊一聊加载Bitmaps图片时的优化原理,但是我还是推荐你直接使用Piacaso或者Glide来加载图片,因为它们已经在底层帮我们优化好了,没有必要重复造轮子。
加载Bitmap进内存
这非常简单,你需要使用BitmapFactory来解码Bitmap就行。
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.hqimage);
imageView.setImageBitmap(bitmap);
看起来一切正常,但实际上这里有一个很严重的问题,我们可以先查看以下加载进来的Bitmap占用的内存大小。
Bitmap.getByteCount()方法可以返回它的大小。使用这个方法获取到最终在内存中的大小是:12262248Bytes,相当于是12.3MB。看到这里,你可能会疑惑了,为什么我在磁盘上看到的文件大小是3.5MB,而使用getByteCount()方法加载到内存中来了之后就大了这么多呢?原因如下:
图片存储在磁盘上的时候,是经过了压缩的(一般是使用JPG,PNG等格式),你一旦加载图片进内存,图片就不再有压缩的效果了。
另外图片加载进内存,Android系统是要根据不同的手机分辨率来进行适配的,这个操作也会导致内存占用增加,具体怎么适配,我后面会单独开一篇文章来讲解。
那么如何优化呢?
- 提前获取图片大小,此时不加载进内存
- 计算图片缩放因子
- 加载图片进内存,此时使用缩放因子进行缩放,内存占用会减少
BitmapFactory.Options
我们可以使用这个类来获取图片的大小,此时图片并没有真正加载进内存。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.mipmap.hqimage, options);
可以看到在传递BitmapFactory.Options实例到BitmapFactory.decodeSource()方法之前,我们把inJustDecodeBounds参数设置成了ture。 inJustDecodeBounds参数的含义就是:只获取图片的信息(宽高等),而不真正加载图片进内存。
当我们把相关信息打印出来后,可以看到如下:
options.outHeight : 1126
options.outWidth : 2000
options.bitmap : null
bitmap为null,证明图片没有加载进内存,而此时我们拿到了图片的宽高信息(这很重要)。
减少内存占用
现在我们可以去计算inSampleSize了,关于inSampleSize,其实就是一个采样率指数,采样率低了,我们最终的内存占用就回降低了。例如我们的原图是1000x1000的,inSampleSize设置为2,你们最终加载进内存的图片就是500x500。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inSampleSize = 3;
BitmapFactory.decodeResource(getResources(), R.mipmap.hqimage, options);
这样就OK了? Too young啊,我们不可能设置一个写死固定的inSampleSize值,而是要根据最终的图标大小来计算这个参数的值。
计算inSampleSize的算法取决于你自己的业务逻辑,你可以根据需要编写自己的算法,下面给出一个Google官方的算法,仅供参考:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
其中的reqWidth和reqHeight参数是目标图片大小。
然后我们要设置inJustDecodeBounds为false,这次要真正加载如内存了,示例代码如下:
options.inSampleSize = calculateInSampleSize(options, 500,500);
options.inJustDecodeBounds = false;
Bitmap smallBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.hqimage, options);
现在,我们再使用bitmap.getByteCount()方法获取大小为3.1MB,我们成功的将内存占用从12.3MB降低到了3.1MB!
减小图片磁盘占用
除了能减小图片的内存占用,我们还能压缩Bitmap,减小图片在磁盘上的占用大小。
首先,不进行压缩优化处理。
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
byte[] bitmapdata = bos.toByteArray();
此时可以发现磁盘上的图片大小为1.6MB,然后进行优化处理,修改compress()的参数。
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, bos);
重新查看大小,这次是24.4KB,我们成功的将图片的磁盘占用从1.6MB降低到了24.4KB!
注意:压缩的格式只能是JPEG,不能是PNG
最终结果如下:
- Android 性能优化之Loading Big Bitmaps
- Android 性能优化之Loading Big Bitmaps
- Android Bitmap之Loading Large Bitmaps Efficiently
- Displaying Bitmaps Efficiently之Loading Large Bitmaps Efficiently
- [Developer Android] Loading Large Bitmaps Efficiently
- Android Managing Bitmap Memory And Loading Large Bitmaps Efficiently
- Loading Large Bitmaps Efficiently
- Loading Large Bitmaps Efficiently
- Loading Large Bitmaps Efficiently
- Loading Large Bitmaps Efficiently
- android之性能优化
- Android之性能优化
- Android之性能优化
- Android之性能优化
- Displaying Bitmaps Efficiently -Loading Large Bitmaps Efficiently
- Android开发之性能优化
- Android之ListView性能优化
- Android开发之性能优化
- 面试题1 -- Java 中,怎么在格式化的日期中显示时区?
- 【读书笔记】Android源码设计模式解析与实战(二)——单例模式
- Java01
- HDU-5734 Acperience(公式化简)
- 通讯方式
- Android 性能优化之Loading Big Bitmaps
- IDEA热部署(一)---解析关键配置。
- 恢复阿里云RDS数据
- 继承、闭包、cookie和session
- Hello World
- java 重试机制总结
- 前台页面与后台servlet实现登录的三种实现方式
- ubuntu 14.04 设置vim tab为4格空格
- apk 反编译