连载 1 - 深入讨论 Android 关于高效显示图片的问题 - 如何高效的加载大位图
2014-05-16 11:07
716 查看
更加详细的说明,可以参阅如下官网地址:http://developer.android.com/training/building-graphics.html
刚开始做 Android 应用时,以为显示图片是很简单的事,在模拟器里运行的好好的,一放到真机上,经常遇到类似于 java.lang.OutofMemoryError: bitmap size exceeds VM budget. 之类的异常。后来看了下官网的详细的介绍,才发现关于图片的显示说头还不少。
下面就把学习心得与大家分享下:
为什么显示图片会遇到很棘手的问题?
手机显示一张 800 万像素的图片(现在主流的手机像素基本上都是 800 万像素以上),大约需要使用 32 MB 的内存,而这刚好是 Android 系统分配给每个应用的最大内存(有的 Android 设备分配给每个应用的最大内存只有 16 MB),所以如果手机应用直接打开这样一张图,基本上都会遇到由于内存溢出而导致程序被迫退出的情况。相信这种情况很多人可能都遇到过。
以 Galaxy Nexus 为例,其后置相机的像素是 500 万,其分辨率为 2592x1936 像素。若位图设置使用的是 ARGB_8888 (在 Android 2.3 及更高版本中,该值为默认值),那么加载该图将占用大约 19 MB 的内存(2592*1936*4 bytes),因此程序很快就会耗尽
Android 分配给每个应用程序的最大内存,从而导致程序崩溃。
即使应用程序不见得非要显示一张 500 万或更高像素的图片,如果程序设计不当,同样会在显示图片时,遇到程序崩溃的问题。例如,在图片相关的应用中,经常需要显示大量图片,因此经合会使用 ListView, GridView 或 ViewPager。若不对显示的图片进行处理,也会由于显示图片过多而导致程序崩溃。
如何解决显示图片导致的内存溢出的问题?
要解决这个问题,需要从以下五点入手:
1. 如何高效的加载大位图。(如何解码大位图,避免超过每个应用允许使用的最大内存)http://yhz61010.iteye.com/blog/1848337
2. 如何在非 UI 线程处理位图。(如何使用 AsyncTask 在后台线程处理位图及处理并发问题)http://yhz61010.iteye.com/blog/1848811
3. 如何对位图进行缓存。(如何通过创建内存缓存和磁盘缓存来流畅的显示多张位图)http://yhz61010.iteye.com/blog/1849645
4. 如何管理位图内存。(如何针对不同的 Android 版本管理位图内存)http://yhz61010.iteye.com/blog/1850232
5. 如何在 UI 中显示位图。(如何通过 ViewPager 和 GridView 显示多张图片)http://yhz61010.iteye.com/blog/1852927
下面我们来一一进行说明。
如何高效的加载大位图?
1. 获取位置尺寸及类型
使用 BitmapFactory 对位图进行解码时,使用 BitmapFactory.Options。将 Options 的 inJustDecodeBounds 设置为 true 时,可以避免为位图分配内存,此时 BitmapFactory.decodeX 的返回结果为 null,但是会为
Options 设置 outWidth, outHeight 和 outMimeType 值。
Java代码
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
通过上述代码,你就可以在不为位图分配内存的情况下,获得位图的宽,高及位图类型。之后在显示图片时,就可以通过获取的信息来判断是否需要对图片进行处理后再显示,从而避免内存溢出问题。
2. 将缩小比例后的图片加载到内存
若程序中仅仅是为了显示一张 128x96 大小的缩略图,而将一张原始大小为 1024x768 的图片加载到内存,就显得很不划算了。因此,对将要显示的图片进行等比例缩小后再进行显示就显得很有必要。
通过设置 options.inSampleSize 来产生缩小后的图片。例如,若 options.inSampleSize = 4,那么对于一张原始大小为 2048x1536 的位图来说,产生的新位图大小约为 512x384。将这个新的位图加载到内存只需要 0.75 MB 内存,而原图则需要占用大约
12 MB 的内存。
具体程序实现如下:
首先,将 inJustDecodeBounds 设置为 true,获取位图信息,然后再设置新的 inSampleSize 值,最后再将 inJustDecodeBounds 设置为 false,从而将新生成的位图加载至内存。
Java代码
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
上述方法中使用的 calculateInSampleSize 方法的实现如下:
Java代码
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) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
需要注意的是,上述方法返回的 inSampleSize 的值,最好是 2 的 n 次幂。(详见http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize)
综上所述,有了上述这些方法,我们就可以在程序中加载任意大小的图片,而不用担心内存溢出的问题。例如,下述代码会将原始图片显示成 100x100 像素的缩略图:
Java代码
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
刚开始做 Android 应用时,以为显示图片是很简单的事,在模拟器里运行的好好的,一放到真机上,经常遇到类似于 java.lang.OutofMemoryError: bitmap size exceeds VM budget. 之类的异常。后来看了下官网的详细的介绍,才发现关于图片的显示说头还不少。
下面就把学习心得与大家分享下:
为什么显示图片会遇到很棘手的问题?
手机显示一张 800 万像素的图片(现在主流的手机像素基本上都是 800 万像素以上),大约需要使用 32 MB 的内存,而这刚好是 Android 系统分配给每个应用的最大内存(有的 Android 设备分配给每个应用的最大内存只有 16 MB),所以如果手机应用直接打开这样一张图,基本上都会遇到由于内存溢出而导致程序被迫退出的情况。相信这种情况很多人可能都遇到过。
以 Galaxy Nexus 为例,其后置相机的像素是 500 万,其分辨率为 2592x1936 像素。若位图设置使用的是 ARGB_8888 (在 Android 2.3 及更高版本中,该值为默认值),那么加载该图将占用大约 19 MB 的内存(2592*1936*4 bytes),因此程序很快就会耗尽
Android 分配给每个应用程序的最大内存,从而导致程序崩溃。
即使应用程序不见得非要显示一张 500 万或更高像素的图片,如果程序设计不当,同样会在显示图片时,遇到程序崩溃的问题。例如,在图片相关的应用中,经常需要显示大量图片,因此经合会使用 ListView, GridView 或 ViewPager。若不对显示的图片进行处理,也会由于显示图片过多而导致程序崩溃。
如何解决显示图片导致的内存溢出的问题?
要解决这个问题,需要从以下五点入手:
1. 如何高效的加载大位图。(如何解码大位图,避免超过每个应用允许使用的最大内存)http://yhz61010.iteye.com/blog/1848337
2. 如何在非 UI 线程处理位图。(如何使用 AsyncTask 在后台线程处理位图及处理并发问题)http://yhz61010.iteye.com/blog/1848811
3. 如何对位图进行缓存。(如何通过创建内存缓存和磁盘缓存来流畅的显示多张位图)http://yhz61010.iteye.com/blog/1849645
4. 如何管理位图内存。(如何针对不同的 Android 版本管理位图内存)http://yhz61010.iteye.com/blog/1850232
5. 如何在 UI 中显示位图。(如何通过 ViewPager 和 GridView 显示多张图片)http://yhz61010.iteye.com/blog/1852927
下面我们来一一进行说明。
如何高效的加载大位图?
1. 获取位置尺寸及类型
使用 BitmapFactory 对位图进行解码时,使用 BitmapFactory.Options。将 Options 的 inJustDecodeBounds 设置为 true 时,可以避免为位图分配内存,此时 BitmapFactory.decodeX 的返回结果为 null,但是会为
Options 设置 outWidth, outHeight 和 outMimeType 值。
Java代码
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
通过上述代码,你就可以在不为位图分配内存的情况下,获得位图的宽,高及位图类型。之后在显示图片时,就可以通过获取的信息来判断是否需要对图片进行处理后再显示,从而避免内存溢出问题。
2. 将缩小比例后的图片加载到内存
若程序中仅仅是为了显示一张 128x96 大小的缩略图,而将一张原始大小为 1024x768 的图片加载到内存,就显得很不划算了。因此,对将要显示的图片进行等比例缩小后再进行显示就显得很有必要。
通过设置 options.inSampleSize 来产生缩小后的图片。例如,若 options.inSampleSize = 4,那么对于一张原始大小为 2048x1536 的位图来说,产生的新位图大小约为 512x384。将这个新的位图加载到内存只需要 0.75 MB 内存,而原图则需要占用大约
12 MB 的内存。
具体程序实现如下:
首先,将 inJustDecodeBounds 设置为 true,获取位图信息,然后再设置新的 inSampleSize 值,最后再将 inJustDecodeBounds 设置为 false,从而将新生成的位图加载至内存。
Java代码
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
上述方法中使用的 calculateInSampleSize 方法的实现如下:
Java代码
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) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
需要注意的是,上述方法返回的 inSampleSize 的值,最好是 2 的 n 次幂。(详见http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize)
综上所述,有了上述这些方法,我们就可以在程序中加载任意大小的图片,而不用担心内存溢出的问题。例如,下述代码会将原始图片显示成 100x100 像素的缩略图:
Java代码
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
相关文章推荐
- 【Android Training - 09】高效地显示Bitmap图片 [ Lesson 1 - 有效率地加载大尺寸的位图]
- 关于Android加载HTML页面中的图片显示问题的解决办法
- Anroid高效显示Bitmap图片,减少OOM问题,加载大尺寸位图
- Android开发中如何解决加载大图片时内存溢出的问题
- Android开发中如何解决加载大图片时内存溢出的问题
- Android开发中如何解决加载大图片时内存溢出的问题
- 关于android示例程序(bitmapfun)——高效加载图片的坑爹地方
- Android开发技巧——TextView加载HTML的图片及代码显示问题
- 【Android 问题集锦】关于android ImageView前景图片显示很小的问题
- 转载关于android高效显示图片的文章---From 移动微技
- 关于android gridview不显示图片,文字等问题
- 关于android示例程序(bitmapfun)——高效加载图片的缺陷
- 关于图片资源在android2.1/2.2/2.3下无法显示的问题以及解决方法
- android图片显示(一) ———— 关于并发,乱序问题的处理
- 关于ListView异步加载图片导致图片显示混乱以及ListView效率问题探讨
- Android开发中如何解决加载大图片时内存溢出的问题
- Android中高效的显示图片之一 ——加载大图
- Android中高效的显示图片之一 ——加载大图
- Android开发技巧——TextView加载HTML的图片及代码显示问题
- 关于ListView中异步加载图片时,图片显示错误的问题