Android Create Bitmap Out Of Memory

本文探讨了Android应用在处理大量图片时容易出现的Out of Memory错误,重点介绍了通过设置BitmapFactory.Options的inSampleSize和inPurgeable属性来优化内存使用,以防止在高分辨率设备上出现OOM问题。

问题

Android 对图片的解码、创建是有内存限制的,在弄一些图片多的程序,不小心很容易出 Out of Memory(OOM)的错误。图片用的内存好像是 native 的内存,由于 4.0 普通 UI 也使用了 GPU 硬件加速,导致系统有不少 UI 的缓冲,所以在高分辨率 4.0 的手机上这个问题更加明显(Galaxy Note、Galaxy Neuxs 等等,估计 native 分配给图片的内存用得差不多了)。在网上找了下资料,发现一个比较有用的方法来避免这个问题。在解码图片的时候,指定一些参数来优化下内存使用情况:BitmapFactory? .Options 。这个是 Android 解码图片的参数。

解决方法

BitmapFactory.Options.inSampleSize

这个值是设置解码图片大小的。 <= 1 的话就是图片原始大小。 > 1 的话就会缩小图片, 例如: 4 就是 1/4 。如果你用的图片大小比原始图片要小的话,合理的设置这个值可以降低系统在解码图片时候所使用的内存。PS:文档说这个值如果是 2^n 次方速度会比较快。BitmapFactory.Options 还有个参数 inJustDecodeBounds 可以让系统只是计算图片的一些信息(原始大小),单是不解码,也就是不占用内存。可以配合这个计算出合适的 inSampleSize。

BitmapFactory.Options.inPurgeable

设置这个值可以让系统在回收内存的时候把图片 pixels 占用的内存回收掉。被回收的图片如果需要再次显示的话,系统会重新解码、载入。

参考代码

上个参考代码吧:
public static Bitmap decodeBitmap(InputStream is, int targetW, int targetH){
        if (null == is) {
                Log.e(TAG, "InputStream is null!");
                return null;
        }
        
        Bitmap bmp = null;
        
        try {
                // decide target image size.
                BitmapFactory.Options bfSizeOp = new BitmapFactory.Options();
                bfSizeOp.inJustDecodeBounds = true;
                
                BitmapFactory.decodeStream(is, null, bfSizeOp);
                
                int scale = 1;
                if (targetW <= 0 && targetH <= 0 ) {
                        scale = 1;
                } else {
                        if (bfSizeOp.outHeight > targetW || bfSizeOp.outWidth > targetH) {
                                scale = (int)Math.pow(2, 
                                (int) Math.round(
                                Math.log(targetW / (double) Math.max(bfSizeOp.outHeight, bfSizeOp.outWidth)) / Math.log(0.5)));
                        }
                }
                
                // decode with inSampleSize and let it auto-gc.
                BitmapFactory.Options bfOp = new BitmapFactory.Options();
                bfOp.inSampleSize = scale;
                bfOp.inPurgeable = true;
                bmp = BitmapFactory.decodeStream(is, null, bfOp);
                
        } catch (Exception e) {
                e.printStackTrace();
                return null;
        }
        
        return bmp;
}

总结

虽然上面的方面可以缓解高分辨率 4.0 手机上创建 bitmap oom 的问题,但是也不能完全依赖这种方法。还需要针对应用进行优化才行。例如说,只持有显示时候的图片,后台不显示的图片要即使释放掉,等等。还有一个有效的方法,在图片多的地方,持有图片使用软件引用(SoftReference<Bitmap> ),这样系统就能够即使的回收不使用的图片的内存,当然你得自己处理图片被回收后,重新加载的问题。

### AndroidBitmap 的使用教程 #### 创建 Bitmap 对象的方式 在 Android 开发中,可以通过多种方式来创建 `Bitmap` 对象。以下是常见的两种方法: 1. **通过 `Bitmap.createBitmap()` 方法** Google 提供了一个静态方法 `createBitmap()` 来创建一个新的位图对象[^1]。此方法有多个重载版本,可以根据不同的需求传入参数。例如: ```java // 根据指定宽度和高度创建一个空白的 Bitmap 对象 Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); ``` 2. **通过 `BitmapFactory.decodeXX` 系列方法** 可以利用 `BitmapFactory` 类中的解码方法加载图片资源并返回对应的 `Bitmap` 对象。这些方法支持从文件、字节数组或流等多种数据源中读取图像。 ```java // 解码一张本地存储的图片文件 Bitmap bitmapFromFile = BitmapFactory.decodeFile(filePath); // 从资源文件中解码图片 Bitmap bitmapFromResource = BitmapFactory.decodeResource(getResources(), R.drawable.image_resource); ``` #### 裁剪 Bitmap 图像 如果需要裁剪现有的 `Bitmap` 对象,可以使用 `Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height)` 方法[^2]。该方法会基于原始位图的一个子区域生成新的位图实例。 ```java // 原始 Bitmap 对象 Bitmap originalBitmap = ...; // 定义裁剪范围 (左上角坐标为 (x,y),宽高分别为 width 和 height) int x = 0; int y = 0; int width = 200; // 新 Bitmap 的宽度 int height = 200; // 新 Bitmap 的高度 // 执行裁剪操作 Bitmap croppedBitmap = Bitmap.createBitmap(originalBitmap, x, y, width, height); ``` #### 设置选项优化内存占用 当处理大尺寸图片时,为了避免内存溢出 (`OutOfMemoryError`),可以在解码前设置 `BitmapFactory.Options` 参数[^3]。其中常用的属性包括 `inSampleSize` 和 `inPreferredConfig`。 - `inSampleSize`: 控制缩放比例,减少像素数量从而降低内存消耗。 - `inPreferredConfig`: 指定目标配置(如 ARGB_8888 或 RGB_565),影响每像素所需的字节大小。 ```java BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; // 预先获取图片尺寸而不实际分配内存 BitmapFactory.decodeResource(getResources(), R.drawable.large_image, options); // 计算合适的采样率 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds = false; // 实际解码图片 Bitmap scaledBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.large_image, options); private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { 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; while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; } ``` #### 常见问题及其解决方案 1. **内存泄漏** 如果未及时回收不再使用的 `Bitmap` 对象,则可能导致内存泄露。建议调用 `recycle()` 方法释放其底层原生资源,并让垃圾回收器尽快清理它。 ```java if (!bitmap.isRecycled()) { bitmap.recycle(); // 显式释放 Bitmap 占用的内存 } ``` 2. **Out of Memory 错误** 加载超大分辨率图片容易引发 OOM 异常。应合理调整 `inSampleSize` 缩减图片规模或者采用分片技术逐步加载部分内容。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值