3

I have a problem of out of memory due to limited virtual memory heap size.
Here is my code for fetching bitmap from server:

@SuppressWarnings("unchecked")
public class DrawableManager {

@SuppressWarnings("rawtypes")
private final Map drawableMap;

    @SuppressWarnings("rawtypes")
    private DrawableManager() {
        drawableMap = new HashMap();
    }

    static private DrawableManager  _instance;

    static public DrawableManager getInstance() {
        if(_instance == null) {
            _instance = new DrawableManager();
        }
        return _instance;
    }

    public Bitmap fetchBitmap(final String sURL) {
        if(sURL.length() == 0)
            return null;
        Bitmap bm = (Bitmap) drawableMap.get(sURL);
        if(bm != null) {
            return bm;
        }

        byte[] imageData = ThumbImg(sURL);

        if(imageData == null)
            return null;

        if(imageData.length > 0) {
            bm =  BitmapFactory.decodeByteArray(imageData, 0, imageData.length);
            if(bm != null) {
                drawableMap.put(sURL, bm);
            }
            return bm;
        }
        else { 
            return null;
        }
    }

    public void fetchBitmapOnThread(final String sURL, final ImageView imageView) {
        if (drawableMap.containsKey(sURL)) {
            imageView.setImageBitmap((Bitmap) drawableMap.get(sURL));
        }

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message message) {
                imageView.setImageBitmap((Bitmap) message.obj);
            }
        };

        Thread thread = new Thread() {
            @Override
            public void run() {
                Bitmap bitmap = fetchBitmap(sURL);
                Message message = handler.obtainMessage(1, bitmap);
                handler.sendMessage(message);
            }
        };
        thread.start();
    }


    @SuppressWarnings("unused")
    public static byte[] ThumbImg(String imgUrl) 
    {

        //first check in the cache, if not available then store in the sd card memory
            HttpURLConnection connection = null;
            String userAgent = null;

            try
            {
                URL url = new URL(imgUrl);
                connection = ( HttpURLConnection ) url.openConnection();
                 if(userAgent != null) {
                     connection.setRequestProperty("User-Agent", userAgent);
                 }
                 connection.setConnectTimeout(5000);
                 connection.setReadTimeout(5000);
                    int CHUNKSIZE = 8192;        //size of fixed chunks
                int BUFFERSIZE = 1024;       //size of reading buffer


                 int bytesRead = 0;
                 byte[] buffer = new byte[BUFFERSIZE];   //initialize buffer
                 byte[] fixedChunk = new byte[CHUNKSIZE]; //initialize 1st chunk
                 ArrayList<byte[]> BufferChunkList = new ArrayList<byte[]>(); // List of chunk data
                 int spaceLeft = CHUNKSIZE;
                 int chunkIndex = 0;

                 DataInputStream in = new DataInputStream(connection.getInputStream() );

                 while( ( bytesRead = in.read( buffer ) ) != -1 ) { //loop until the DataInputStream is completed
                     if(bytesRead > spaceLeft) {
                         //copy to end of current chunk
                         System.arraycopy(buffer, 0, fixedChunk, chunkIndex, spaceLeft);
                         BufferChunkList.add(fixedChunk);

                         //create a new chunk, and fill in the leftover
                         fixedChunk = new byte[CHUNKSIZE];
                         chunkIndex = bytesRead - spaceLeft;
                         System.arraycopy(buffer, spaceLeft, fixedChunk, 0, chunkIndex);
                     } else {
                         //plenty of space, just copy it in
                         System.arraycopy(buffer, 0, fixedChunk, chunkIndex, bytesRead);
                         chunkIndex = chunkIndex + bytesRead;
                     }
                     spaceLeft = CHUNKSIZE - chunkIndex;
                 }

                 if (in != null) {
                     in.close();
                 }

                 // copy it all into one big array
                 int responseSize = (BufferChunkList.size() * CHUNKSIZE) + chunkIndex;  
                 Log.d("response size",""+responseSize);
                 byte[] responseBody = new byte[responseSize];
                 int index = 0;
                 for(byte[] b : BufferChunkList) {
                     System.arraycopy(b, 0, responseBody, index, CHUNKSIZE);
                     index = index + CHUNKSIZE;
                 }
                 System.arraycopy(fixedChunk, 0, responseBody, index, chunkIndex);

                return responseBody;                     


            }catch(SocketTimeoutException se)
            {

            }
            catch(Exception e)
            {

                e.printStackTrace();
            }finally
            {
                if(connection!=null)
                connection.disconnect();
            }

        return null;
    }

}

This is the code that I am using, this works fine with smaller images but not for large images. What is the problem and to solve it?

Thanks

Nikki
  • 3,314
  • 13
  • 36
  • 59

2 Answers2

6

Its not always a good idea to store bitmaps in memory. If you really want to do so then try using SoftReference for your map. Check this

make your map's value argument as SoftReference<Bitmap>. Then while searching in the map use this code snippet

@SuppressWarnings("unchecked")
public class DrawableManager {

@SuppressWarnings("rawtypes")
private final Map<String, SoftReference<Bitmap>> drawableMap;

    @SuppressWarnings("rawtypes")
    private DrawableManager() {
        drawableMap = new HashMap<String, SoftReference<Bitmap>>();
    }

    static private DrawableManager  _instance;

    static public DrawableManager getInstance() {
        if(_instance == null) {
            _instance = new DrawableManager();
        }
        return _instance;
    }

    public Bitmap fetchBitmap(final String sURL) {
        if(sURL.length() == 0)
            return null;
        Bitmap bm = null;
            SoftReference<Bitmap> reference = drawbaleM.get(imagePath);                  
            if(reference != null) bm = reference.get();
            if(bm != null) {
             return bm;
            }

        byte[] imageData = ThumbImg(sURL);

        if(imageData == null)
            return null;

        if(imageData.length > 0) {
            bm =  BitmapFactory.decodeByteArray(imageData, 0, imageData.length);
            if(bm != null) {
                drawableMap.put(sURL, bm);
            }
            return bm;
        }
        else { 
            return null;
        }
    }

    public void fetchBitmapOnThread(final String sURL, final ImageView imageView) {
        if (drawableMap.containsKey(sURL)) {
            imageView.setImageBitmap((Bitmap) drawableMap.get(sURL));
        }

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message message) {
                imageView.setImageBitmap((Bitmap) message.obj);
            }
        };

        Thread thread = new Thread() {
            @Override
            public void run() {
                Bitmap bitmap = fetchBitmap(sURL);
                Message message = handler.obtainMessage(1, bitmap);
                handler.sendMessage(message);
            }
        };
        thread.start();
    }


    @SuppressWarnings("unused")
    public static byte[] ThumbImg(String imgUrl) 
    {

        //first check in the cache, if not available then store in the sd card memory
            HttpURLConnection connection = null;
            String userAgent = null;

            try
            {
                URL url = new URL(imgUrl);
                connection = ( HttpURLConnection ) url.openConnection();
                 if(userAgent != null) {
                     connection.setRequestProperty("User-Agent", userAgent);
                 }
                 connection.setConnectTimeout(5000);
                 connection.setReadTimeout(5000);
                    int CHUNKSIZE = 8192;        //size of fixed chunks
                int BUFFERSIZE = 1024;       //size of reading buffer


                 int bytesRead = 0;
                 byte[] buffer = new byte[BUFFERSIZE];   //initialize buffer
                 byte[] fixedChunk = new byte[CHUNKSIZE]; //initialize 1st chunk
                 ArrayList<byte[]> BufferChunkList = new ArrayList<byte[]>(); // List of chunk data
                 int spaceLeft = CHUNKSIZE;
                 int chunkIndex = 0;

                 DataInputStream in = new DataInputStream(connection.getInputStream() );

                 while( ( bytesRead = in.read( buffer ) ) != -1 ) { //loop until the DataInputStream is completed
                     if(bytesRead > spaceLeft) {
                         //copy to end of current chunk
                         System.arraycopy(buffer, 0, fixedChunk, chunkIndex, spaceLeft);
                         BufferChunkList.add(fixedChunk);

                         //create a new chunk, and fill in the leftover
                         fixedChunk = new byte[CHUNKSIZE];
                         chunkIndex = bytesRead - spaceLeft;
                         System.arraycopy(buffer, spaceLeft, fixedChunk, 0, chunkIndex);
                     } else {
                         //plenty of space, just copy it in
                         System.arraycopy(buffer, 0, fixedChunk, chunkIndex, bytesRead);
                         chunkIndex = chunkIndex + bytesRead;
                     }
                     spaceLeft = CHUNKSIZE - chunkIndex;
                 }

                 if (in != null) {
                     in.close();
                 }

                 // copy it all into one big array
                 int responseSize = (BufferChunkList.size() * CHUNKSIZE) + chunkIndex;  
                 Log.d("response size",""+responseSize);
                 byte[] responseBody = new byte[responseSize];
                 int index = 0;
                 for(byte[] b : BufferChunkList) {
                     System.arraycopy(b, 0, responseBody, index, CHUNKSIZE);
                     index = index + CHUNKSIZE;
                 }
                 System.arraycopy(fixedChunk, 0, responseBody, index, chunkIndex);

                return responseBody;                     


            }catch(SocketTimeoutException se)
            {

            }
            catch(Exception e)
            {

                e.printStackTrace();
            }finally
            {
                if(connection!=null)
                connection.disconnect();
            }

        return null;
    }

}

Please note this doesnot guarantee relief from OOM. It is not always a good idea to show large bitmaps.

Another option you can go after is use BitmapFactory.Options inSampleSize argument

Community
  • 1
  • 1
pankajagarwal
  • 13,462
  • 14
  • 54
  • 65
  • @frieza: but where and how to use SoftReferences in my code. Can you please explain – Nikki Mar 16 '11 at 06:11
  • @frieza: Can you please edit your sample code in mine code as this is giving error on my side while making changes according your sample code – Nikki Mar 16 '11 at 07:13
  • @frieza: But it is showing error in drawbleM.get() as not able to cast in sofwarereferense – Nikki Mar 16 '11 at 07:59
  • As I have also used this as : private DrawableManager() { drawableMap = new HashMap(); } where DrawableManager is class and calling constructor – Nikki Mar 16 '11 at 08:01
  • Sorry but what you are saying is not clear. Can you post your code – pankajagarwal Mar 16 '11 at 08:26
  • @frieza: Did you find any solution to this problem as I have edited my code – Nikki Mar 16 '11 at 11:28
  • @frieza: Using BitmapFactory.Options inSampleSize reduces the quality of image as it streches the image ......how can i regain the original look through this option – Nikki Mar 16 '11 at 11:53
  • Not possible, you need to compromise with something when working with large bitmaps. By the way did SoftReference work to some extent ? – pankajagarwal Mar 16 '11 at 12:07
  • @frieza:while adding your code provided above.....It is giving error in this line imageView.setImageBitmap((Bitmap) drawableMap.get(sURL)); as can't convert softwareRefernce to bitmap – Nikki Mar 16 '11 at 12:09
  • @frieza: showing error at most places where used bm as to convert in SoftwareRefernces – Nikki Mar 16 '11 at 12:11
  • dude when you do `drawableMap.get(sURL)` it returns a `SoftReference` for a bitmap. From that `SoftReference` object (lets call it `reference` and please check for null) you need to call `reference.get()` which will return you the bitmap. You can't directly cast a SoftRefernce object to bitmap – pankajagarwal Mar 16 '11 at 12:15
  • @frieza: how to cast drawableM from SoftwareReference to bitmap – Nikki Mar 16 '11 at 12:34
  • see this code `Bitmap bm = null; SoftReference reference = drawbaleM.get(imagePath); if(reference != null) bm = reference.get(); if(bm != null) { return bm; }` – pankajagarwal Mar 16 '11 at 12:39
  • drawableM is a HashMap, you can't cast HashMap to Bitmap. see my previous comment carefully. It clearly says that when you call the get(sURL) method on your map, it returns you a SoftRefernce object. You can consider this object as a container for your bitmap. call the get() method on this SoftRefernce object to get your bitmap. I have no other better way to explain this further. Also you can look for Softrefernce documentation. Also please see this link for further clarification http://developer.android.com/resources/samples/XmlAdapters/src/com/example/android/xmladapters/ImageDownloader.html – pankajagarwal Mar 16 '11 at 13:00
  • @frieza: ok! thanks for your so much help. I hope this will help me out of my problem – Nikki Mar 16 '11 at 13:14
  • Dalvik's garbage collector is very eager to collect soft referenced objects, so Google recommends using their LruCache instead. – kaka Aug 26 '12 at 22:05
0

BitmapFactory.decodeByteArray() creates the bitmap data in the Native heap, not the VM heap. See BitmapFactory OOM driving me nuts for details.

You could monitor the Native heap (as per BitmapFactory OOM driving me nuts) and downsample if you're running short of space.

Community
  • 1
  • 1
Torid
  • 4,176
  • 1
  • 28
  • 29
  • Can you please tell me where and how to include this sample in my code (as shown above) to run – Nikki Mar 31 '11 at 05:38
  • Based on the SO response linked, since Honeycomb Bitmap _are_ allocated on the VM heap. So this is not a solution anymore if your minSDK is more than 13 – Gregriggins36 Apr 16 '18 at 10:53