【问题标题】:Problem with big images ( java.lang.OutOfMemoryError: bitmap size exceeds VM budget )大图像问题(java.lang.OutOfMemoryError:位图大小超出 VM 预算)
【发布时间】:2011-07-14 12:24:38
【问题描述】:

作为许多人,我也有大图像的问题。 好的,这是我的代码。我在 stackoverflow 上找到了如何使用 BitmapFactory.Options 来解决这个问题。但在我的情况下,我不是从文件中获取图像。谁能告诉我如何使用已经在内存中创建的位图来做到这一点(我从手机上的相机中得到这张照片)

 public void getImageAndSend( )
  {

     Bitmap newbmp ;
         newbmp= Bitmap.createBitmap(oBmp.getWidth(), oBmp.getHeight(),oBmp.getConfig());////<----------error here
      log("widt oBmp = "+oBmp.getWidth());/// width = 2048
      log(" height oBmp = "+oBmp.getHeight());///height 1538
      //BitmapFactory.Options options = new BitmapFactory.Options(); // <----- it's possible to do it without `BitmapFactory.decodeFile(pathToFile,options) ?? 
      //options.inTempStorage = new byte[16*1024];
          Canvas canvas = new Canvas(newbmp);
          //code 
  System.gc();/// can't right understand but this line fixed this problem on emulator but not on real device   

}

请告诉我如何解决我的问题? : ( 在模拟器上我没有这个错误。但是在真实设备上我看到了这个错误(位图大小超过了 VM 预算) 问候,彼得。

【问题讨论】:

    标签: android bitmap out-of-memory bitmapfactory


    【解决方案1】:

    由于您是从相机获取图像,因此在 onActivityResult() 方法中获取您捕获的图像的 uri,如下所示

    Uri selectedImageUri=intent.getData();
    String actualPath=getRealPathFromURI(selectedImageUri);
    File file=new File(actualPath);
    Bitmap bitmap=decodeFile(file); //this is new bitmap which you can use for your purpose 
    
    public String getRealPathFromURI(Uri contentUri) {
            String[] proj = { MediaStore.Images.Media.DATA };
            Cursor cursor = managedQuery(contentUri, proj, null, null, null);
            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();
            return cursor.getString(column_index);
        }
    
    
    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 width_tmp=o.outWidth, height_tmp=o.outHeight;
                int scale=1;
                while(true){
                    if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
                        break;
                    width_tmp/=2;
                    height_tmp/=2;
                    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;
        }
    

    希望这会有所帮助。这是您处理从手机拍摄的高质量图像时非常熟悉的问题。也请检查this

    【讨论】:

    • oBmp - 我的位图,我从 byteArray 获得,而不是从意图获得。位图 bmp=BitmapFactory.decodeByteArray(bytes,0,bytes.length);我可以在这里使用选项吗??
    • 你说你从照相手机获取图像。不是吗?那你能详细说明你从那里做什么吗?那你为什么需要从字节数组中获取它?
    • public void onPictureTaken(byte[] paramArrayOfByte, Camera paramCamera) {}
    • 我尝试使用 byteArray (不是来自文件),现在我没有错误 - 但质量非常糟糕。图片模糊。
    • @androidGuy 我得到空位图..??
    【解决方案2】:

    这是我的 BitmpaHelper 类,如果 OutOfMemoryError 证明 :-)

    import java.io.File;
    import java.io.FileInputStream;
    
    import android.graphics.Bitmap;
    import android.graphics.Bitmap.Config;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Matrix;
    import android.graphics.drawable.BitmapDrawable;
    import android.graphics.drawable.Drawable;
    
    public class BitmapHelper
    {
    
        //decodes image and scales it to reduce memory consumption
        public static Bitmap decodeFile(File bitmapFile, int requiredWidth, int requiredHeight, boolean quickAndDirty)
        {
            try
            {
                //Decode image size
                BitmapFactory.Options bitmapSizeOptions = new BitmapFactory.Options();
                bitmapSizeOptions.inJustDecodeBounds = true;
                BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapSizeOptions);
    
                // load image using inSampleSize adapted to required image size
                BitmapFactory.Options bitmapDecodeOptions = new BitmapFactory.Options();
                bitmapDecodeOptions.inTempStorage = new byte[16 * 1024];
                bitmapDecodeOptions.inSampleSize = computeInSampleSize(bitmapSizeOptions, requiredWidth, requiredHeight, false);
                bitmapDecodeOptions.inPurgeable = true;
                bitmapDecodeOptions.inDither = !quickAndDirty;
                bitmapDecodeOptions.inPreferredConfig = quickAndDirty ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888;
    
                Bitmap decodedBitmap = BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapDecodeOptions);
    
                // scale bitmap to mathc required size (and keep aspect ratio)
    
                float srcWidth = (float) bitmapDecodeOptions.outWidth;
                float srcHeight = (float) bitmapDecodeOptions.outHeight;
    
                float dstWidth = (float) requiredWidth;
                float dstHeight = (float) requiredHeight;
    
                float srcAspectRatio = srcWidth / srcHeight;
                float dstAspectRatio = dstWidth / dstHeight;
    
                // recycleDecodedBitmap is used to know if we must recycle intermediary 'decodedBitmap'
                // (DO NOT recycle it right away: wait for end of bitmap manipulation process to avoid
                // java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@416ee7d8
                // I do not excatly understand why, but this way it's OK
    
                boolean recycleDecodedBitmap = false;
    
                Bitmap scaledBitmap = decodedBitmap;
                if (srcAspectRatio < dstAspectRatio)
                {
                    scaledBitmap = getScaledBitmap(decodedBitmap, (int) dstWidth, (int) (srcHeight * (dstWidth / srcWidth)));
                    // will recycle recycleDecodedBitmap
                    recycleDecodedBitmap = true;
                }
                else if (srcAspectRatio > dstAspectRatio)
                {
                    scaledBitmap = getScaledBitmap(decodedBitmap, (int) (srcWidth * (dstHeight / srcHeight)), (int) dstHeight);
                    recycleDecodedBitmap = true;
                }
    
                // crop image to match required image size
    
                int scaledBitmapWidth = scaledBitmap.getWidth();
                int scaledBitmapHeight = scaledBitmap.getHeight();
    
                Bitmap croppedBitmap = scaledBitmap;
    
                if (scaledBitmapWidth > requiredWidth)
                {
                    int xOffset = (scaledBitmapWidth - requiredWidth) / 2;
                    croppedBitmap = Bitmap.createBitmap(scaledBitmap, xOffset, 0, requiredWidth, requiredHeight);
                    scaledBitmap.recycle();
                }
                else if (scaledBitmapHeight > requiredHeight)
                {
                    int yOffset = (scaledBitmapHeight - requiredHeight) / 2;
                    croppedBitmap = Bitmap.createBitmap(scaledBitmap, 0, yOffset, requiredWidth, requiredHeight);
                    scaledBitmap.recycle();
                }
    
                if (recycleDecodedBitmap)
                {
                    decodedBitmap.recycle();
                }
                decodedBitmap = null;
    
                scaledBitmap = null;
                return croppedBitmap;
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
            return null;
        }
    
        /**
         * compute powerOf2 or exact scale to be used as {@link BitmapFactory.Options#inSampleSize} value (for subSampling)
         * 
         * @param requiredWidth
         * @param requiredHeight
         * @param powerOf2
         *            weither we want a power of 2 sclae or not
         * @return
         */
        public static int computeInSampleSize(BitmapFactory.Options options, int dstWidth, int dstHeight, boolean powerOf2)
        {
            int inSampleSize = 1;
    
            // Raw height and width of image
            final int srcHeight = options.outHeight;
            final int srcWidth = options.outWidth;
    
            if (powerOf2)
            {
                //Find the correct scale value. It should be the power of 2.
    
                int tmpWidth = srcWidth, tmpHeight = srcHeight;
                while (true)
                {
                    if (tmpWidth / 2 < dstWidth || tmpHeight / 2 < dstHeight)
                        break;
                    tmpWidth /= 2;
                    tmpHeight /= 2;
                    inSampleSize *= 2;
                }
            }
            else
            {
                // Calculate ratios of height and width to requested height and width
                final int heightRatio = Math.round((float) srcHeight / (float) dstHeight);
                final int widthRatio = Math.round((float) srcWidth / (float) dstWidth);
    
                // 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;
        }
    
        public static Bitmap drawableToBitmap(Drawable drawable)
        {
            if (drawable instanceof BitmapDrawable)
            {
                return ((BitmapDrawable) drawable).getBitmap();
            }
    
            Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888);
            Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);
    
            return bitmap;
        }
    
        public static Bitmap getScaledBitmap(Bitmap bitmap, int newWidth, int newHeight)
        {
            int width = bitmap.getWidth();
            int height = bitmap.getHeight();
            float scaleWidth = ((float) newWidth) / width;
            float scaleHeight = ((float) newHeight) / height;
    
            // CREATE A MATRIX FOR THE MANIPULATION
            Matrix matrix = new Matrix();
            // RESIZE THE BIT MAP
            matrix.postScale(scaleWidth, scaleHeight);
    
            // RECREATE THE NEW BITMAP
            Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
            return resizedBitmap;
        }
    
    }
    

    【讨论】:

      【解决方案3】:

      我的安卓应用也有同样的问题。当您从大尺寸图像解码位图并将 imageBitmap 设置为图像视图时,您的应用程序可能会变慢,经过几次尝试后,您会收到“内存不足异常”

      您可以使用通用图像加载器。 https://github.com/nostra13/Android-Universal-Image-Loader

      String url = "file://" + your_file_path_on_sd_card;
      com.nostra13.universalimageloader.core.ImageLoader.getInstance().displayImage(url, ivPicture, options);
      

      【讨论】:

        猜你喜欢
        • 2012-08-19
        • 2011-04-26
        • 1970-01-01
        • 2010-12-29
        相关资源
        最近更新 更多