位图内存不足问题

5

我的问题

我用安卓设备拍了一张照片,然后从文件中解码这张照片。

        Bitmap photo = BitmapFactory.decodeFile(EXTERNAL_IMAGE_PATH+File.separator+this._currentPhotoName+JPEG_FILE_SUFFIX);
        if (photo == null && data != null) 
            photo = (Bitmap) data.getExtras().get("data");
        else if (data == null && photo == null)
            Log.e("CCPhotoManager","Can't find image from file or from intent data.");

我会检查这张图片并判断是否需要旋转到正确的方向。

             try {
                ExifInterface exif = new ExifInterface(EXTERNAL_IMAGE_PATH+File.separator+this._currentPhotoName+JPEG_FILE_SUFFIX);
                int rotation = CCDataUtils.exifToDegrees(exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL));
                Log.v("CCPhotoManager", "Rotation:"+rotation);
                if (rotation > 0) {
                    photo = this.convertSavedImageToCorrectOrientation(EXTERNAL_IMAGE_PATH+File.separator+this._currentPhotoName+JPEG_FILE_SUFFIX, photo, rotation);
                }
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }

如果需要旋转,我会调用这个方法。
public Bitmap convertSavedImageToCorrectOrientation(String filePath,Bitmap photo,int rotation) {
        Log.d("CCPhotoManager", "Changing Orientation of photo located at: "+filePath+" Rotating by:"+rotation);
        int width = photo.getWidth();
        int height = photo.getHeight();


        Matrix matrix = new Matrix();
        matrix.preRotate(rotation);

        Bitmap adjusted = Bitmap.createBitmap(photo, 0, 0, width, height, matrix, true);

        try {
               FileOutputStream out = new FileOutputStream(filePath);
               adjusted.compress(Bitmap.CompressFormat.JPEG, 100, out);
        } catch (Exception e) {
               e.printStackTrace();
        }

        return adjusted;
    }

如果在Bitmap adjusted = Bitmap.createBitmap(photo,0,0,width,height,matrix,true);这一行调用convertSavedImageToCorrectOrientation,那么我会收到内存不足的投诉。

这只发生在三星Galaxy S3上。在三星Galaxy AceHTC HeroSony Xperia U上都可以正常工作。

以下是错误信息。

10-17 14:33:33.950: E/AndroidRuntime(12556): java.lang.OutOfMemoryError
10-17 14:33:33.950: E/AndroidRuntime(12556):    at android.graphics.Bitmap.nativeCreate(Native Method)
10-17 14:33:33.950: E/AndroidRuntime(12556):    at android.graphics.Bitmap.createBitmap(Bitmap.java:605)
10-17 14:33:33.950: E/AndroidRuntime(12556):    at android.graphics.Bitmap.createBitmap(Bitmap.java:551)

而且这是一大笔内存。

10-17 14:33:33.945: E/dalvikvm-heap(12556): Out of memory on a 31961104-byte allocation.

我认为这与Bitmap的数量有关,但我不确定如何防止出现此错误。

我知道你可以调用.recycle();,但似乎并没有起作用。

我的问题

我应该如何正确处理Bitmap,以避免出现OOM问题?

提前致谢


你需要读取、修改并保存整个图像吗?没有样本大小?可能是因为S3相机更好,因此您在内存中加载了更大的图片。 - nsemeniuk
1
@nsemeniuk 我需要将整个图像旋转到正确的方向并重新保存,因为相机总是以其默认方向保存它,而这并不总是正确的。我相当确定由于更好的相机,图像的大小是导致这种情况的原因,就像你所说的那样。 - StuStirling
好的,相机是这样保存的,但是如果您需要在应用程序上使用该图片,则始终可以使用 EXIF 数据正确地显示它... 当然,尺寸会小一些。 - nsemeniuk
这是一个很好的建议和一个不错的替代方案。我对这种方法的问题在于当我需要正确方向的实际文件时,例如将其附加到电子邮件中。 - StuStirling
我在另一个类似的帖子中发布的答案可能会对您有所帮助:https://dev59.com/puo6XIcBkEYKwwoYTzNK#18544069 - Bruce
显示剩余3条评论
3个回答

4

对于内存不足的问题

//解码图像并按比例缩小以减少内存消耗

private Bitmap decodeFile(File f){

try {
    //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(new FileInputStream(f),null,o);

    //The new size we want to scale to
    final int REQUIRED_SIZE=70;

    //Find the correct scale value. It should be the power of 2.
    int scale=1;
    while(o.outWidth/scale/2>=REQUIRED_SIZE && o.outHeight/scale/2>=REQUIRED_SIZE)
        scale*=2;

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize=scale;
    return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}

如果我对图像进行缩放然后再保存,那保存后的图像会比原始图像小吗? - StuStirling
这里的size变量是多少?是原始大小的70%吗? - StuStirling
那么这是原始大小的百分比吗?我目前正在尝试实现它。 - StuStirling
谢谢这个方法。非常有帮助。只需要找到一种方法来获取所需的比例,使其看起来仍然不错。 - StuStirling
抱歉,我想在之前的评论中补充一点。有没有办法找到我想要缩放的确切尺寸,例如我的手机屏幕宽度?如果我的图像不是正方形怎么办? - StuStirling
显示剩余3条评论

4
我曾经遇到过完全相同的问题,使用完全相同的设备进行完全相同的操作!不幸的是,在我的情况下,需要使用完整尺寸的原始图像将其提交到Web服务。对我有用的是在清单文件的应用程序元素中打开largeHeap
这并不能永久解决问题-可能会出现一个甚至更大的相机设备,即使启用了largeHeap,图片也无法适应内存。为了捕捉这个极端情况,我还在旋转图像的代码周围放置了try catch,并向用户显示了一个漂亮的错误。
更完整的解决方案是编写自己的jpeg操作代码,可以使用基于流的方法旋转jpeg,以便图像永远不需要加载到内存中。

3

这里是一个更完整的示例,展示了如何调整大小/旋转图片。该示例部分引用自Android开发者指南(请更改REQ_WIDTH和REQ_HEIGHT):

private static final int REQ_WIDTH = 450;
private static final int REQ_HEIGHT = 450;

/**
 * Resize, crop, rotate and Inserts the picture on the layout.
 * 
 * @param mImageView to insert the bitmap.
 * @param imageURI from wich to obtain the bitmap.
 * 
 */
private void setPic(ImageView mImageView, String imageURI) {
    // Get the original bitmap dimensions
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imageURI, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, REQ_HEIGHT, REQ_WIDTH);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    Bitmap bitmap = BitmapFactory.decodeFile(imageURI, options);

    //need rotation?
    float rotation = rotationForImage(getActivity(), Uri.fromFile(new File(imageURI)));

    if (rotation != 0) {
        //rotate
        Matrix matrix = new Matrix();
        matrix.preRotate(rotation);
        mImageView.setImageBitmap(Bitmap.createBitmap(bitmap, 0, 0, REQ_HEIGHT, REQ_WIDTH, matrix, true));
    } else {
        //use the original
        mImageView.setImageBitmap(BitmapFactory.decodeFile(imageURI, options));
    }
}

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) {
        if (width > height) {
            inSampleSize = Math.round((float) height / (float) reqHeight);
        } else {
            inSampleSize = Math.round((float) width / (float) reqWidth);
        }
    }
    return inSampleSize;
}

public static float rotationForImage(Context context, Uri uri) {
    try {
        if (uri.getScheme().equals("content")) {
            String[] projection = { Images.ImageColumns.ORIENTATION };
            Cursor c = context.getContentResolver().query(uri, projection, null, null, null);
            if (c.moveToFirst()) {
                return c.getInt(0);
            }
        } else if (uri.getScheme().equals("file")) {
            ExifInterface exif = new ExifInterface(uri.getPath());
            int rotation = (int) exifOrientationToDegrees(exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL));
            return rotation;
        }
        return 0;

    } catch (IOException e) {
        Log.e(TAG, "Error checking exif", e);
        return 0;
    }
}

private static float exifOrientationToDegrees(int exifOrientation) {
    if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
        return 90;
    } else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
        return 180;
    } else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
        return 270;
    }
    return 0;
}

最佳的SO答案适用于此情况。 - Harpreet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接