OutOfMemory exception when loading bitmap from external storage

前端 未结 13 1703
广开言路
广开言路 2020-11-29 06:52

In my application I load a couple of images from JPEG and PNG files. When I place all those files into assets directory and load it in this way, everything is ok:

         


        
相关标签:
13条回答
  • 2020-11-29 07:20

    Use the below code and you will never get the following error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget

                  BitmapFactory.Options bounds = new BitmapFactory.Options();
    
                  bounds.inSampleSize = 4;
    
                  myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bounds);
    
                  picturesView.setImageBitmap(myBitmap);
    
    0 讨论(0)
  • 2020-11-29 07:32

    One of the most common errors that I found developing Android Apps is the “java.lang.OutOfMemoryError: Bitmap Size Exceeds VM Budget” error. I found this error frecuently on activities using lots of bitmaps after changing orientation: the Activity is destroyed, created again and the layouts are “inflated” from the XML consuming the VM memory avaiable for bitmaps.

    Bitmaps on the previous activity layout are not properly deallocated by the garbage collector because they have crossed references to their activity. After many experiments I found a quite good solution for this problem.

    First, set the “id” attribute on the parent view of your XML layout:

        <?xml version="1.0" encoding="utf-8"?>
        <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
         android:id="@+id/RootView"
         >
         ...
    

    Then, on the onDestroy() method of your Activity, call the unbindDrawables() method passing a refence to the parent View and then do a System.gc()

        @Override
        protected void onDestroy() {
        super.onDestroy();
    
        unbindDrawables(findViewById(R.id.RootView));
        System.gc();
        }
    
        private void unbindDrawables(View view) {
            if (view.getBackground() != null) {
            view.getBackground().setCallback(null);
            }
            if (view instanceof ViewGroup) {
                for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
                unbindDrawables(((ViewGroup) view).getChildAt(i));
                }
            ((ViewGroup) view).removeAllViews();
            }
        }
    

    This unbindDrawables() method explores the view tree recursively and:

    1. Removes callbacks on all the background drawables
    2. Removes childs on every viewgroup
    0 讨论(0)
  • 2020-11-29 07:33
    The best solution i found and edited according to my need
    
    public static Bitmap getImageBitmap(String path) throws IOException{
            // Allocate files and objects outside of timingoops             
            File file = new File(thumbpath);        
            RandomAccessFile in = new RandomAccessFile(file, "rws");
            final FileChannel channel = in.getChannel();
            final int fileSize = (int)channel.size();
            final byte[] testBytes = new byte[fileSize];
            final ByteBuffer buff = ByteBuffer.allocate(fileSize);
            final byte[] buffArray = buff.array();
            @SuppressWarnings("unused")
            final int buffBase = buff.arrayOffset();
    
            // Read from channel into buffer, and batch read from buffer to byte array;
            long time1 = System.currentTimeMillis();
            channel.position(0);
            channel.read(buff);
            buff.flip();
            buff.get(testBytes);
            long time1 = System.currentTimeMillis();
            Bitmap bmp = Bitmap_process(buffArray);
            long time2 = System.currentTimeMillis();        
            System.out.println("Time taken to load: " + (time2 - time1) + "ms");
    
            return bmp;
        }
    
        public static Bitmap Bitmap_process(byte[] buffArray){
            BitmapFactory.Options options = new BitmapFactory.Options();
    
            options.inDither=false;                     //Disable Dithering mode
            options.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
            options.inInputShareable=true;              //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
            options.inTempStorage=new byte[32 * 1024];  //Allocate some temporal memory for decoding
    
            options.inSampleSize=1;
    
            Bitmap imageBitmap = BitmapFactory.decodeByteArray(buffArray, 0, buffArray.length, options);
            return imageBitmap;
        }
    
    0 讨论(0)
  • 2020-11-29 07:34

    Instead of loading it from the SD Card directly, why not move the image to the cache in the phone's internal storage using getCacheDir() or use a temp directory to store the images in?

    See this, this on external memory usage. Also, this article may be of relevance to you.

    0 讨论(0)
  • 2020-11-29 07:35

    Try this another way...

    Bitmap bmpOrignal = BitmapFactory.decodeFile("/sdcard/mydata/" + path");
    
    0 讨论(0)
  • 2020-11-29 07:37

    Probably nothing wrong with your API usage, I guess all we can do is infer that using the AssetManager involves less behind-the-scenes heap allocation than opening a random file from the SD card.

    800KB is a serious allocation in anybody's book... this will doubtless be for the decompressed image pixels. Given that you know the size of the image, what depth is it? If it's 32bpp then try overriding that using inPreferredConfig.

    0 讨论(0)
提交回复
热议问题