缩小android位图而不将它们保存为jpeg文件以避免内存不足错误

时间:2013-12-09 00:40:16

标签: android bitmap android-camera out-of-memory bitmapfactory

我仍然围绕着位图如何在Android上工作。我目前的情况是 - 我正在尝试显示图像的网格视图,但是大约8张左右的图片会出现内存错误。

我现在的方式是使用自定义相机(扩展surfaceview 并实现surfaceholder.callback)来拍照,使用onPreviewFrame方法解码字节数组将YUV420SP转换函数转换为int []数组,然后从该数组创建位图。我这样做有两个原因,1。将流保存到sqlite数据库中的blob中以便稍后传输到服务器,以及2.避免将其保存为手机中可以更改的jepg用。

如果有更好的方法可以做到这一点,我愿意听到它,但是现在我正在寻找一个答案,当我从数据库中调用它后显示它时,它会帮助我缩小图像大小,或在将其保存到数据库之前。我已经查看了关于这个主题的其他主题,但到目前为止我看到的所有内容都需要将其保存为jpeg,然后使用bitmapfactory.options将文件位置解码为位图。有没有人知道一种方法直接使用给定的字节数组参数,甚至在使用YUV420SP进行转换后?

如果需要进一步澄清,请告诉我。

1 个答案:

答案 0 :(得分:0)

所以问题是你没有在将图像显示到GridView之前缩小图像,所以当你从那些int []创建Bitmap时,你应该在显示Bitmap之前创建一个缩小版本。

会有这样的事情:

public static byte[] intArraysToByteArrays(int[] data){
    ByteBuffer byteBuffer = ByteBuffer.allocate(data.length * 4);        
    IntBuffer intBuffer = byteBuffer.asIntBuffer();
    intBuffer.put(data);

    return byteBuffer.array();
}

public static Bitmap decodeSampleBitmapFromByteStream(byte[] data , int reqWidth, int reqHeight){
     // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeByteArray(data, 0, data.length, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;

    return BitmapFactory.decodeByteArray(data, 0, data.length, options);
}


/**
 * Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
 * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
 * the closest inSampleSize that will result in the final decoded bitmap having a width and
 * height equal to or larger than the requested width and height. This implementation does not
 * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
 * results in a larger bitmap which isn't as useful for caching purposes.
 *
 * @param options An options object with out* params already populated (run through a decode*
 *            method with inJustDecodeBounds==true
 * @param reqWidth The requested width of the resulting bitmap
 * @param reqHeight The requested height of the resulting bitmap
 * @return The value to be used for inSampleSize
 */
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;

        // This offers some additional logic in case the image has a strange
        // aspect ratio. For example, a panorama may have a much larger
        // width than height. In these cases the total pixels might still
        // end up being too large to fit comfortably in memory, so we should
        // be more aggressive with sample down the image (=larger inSampleSize).

        final float totalPixels = width * height;

        // Anything more than 2x the requested pixels we'll sample down further
        final float totalReqPixelsCap = reqWidth * reqHeight * 2;

        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
            inSampleSize++;
        }
    }
    return inSampleSize;
}

注意:我没有测试过代码,但我相信它应该可以正常工作。为了获得更好的性能,所有这些都应该在后台线程中运行。