2

I have been using Image loader class for lazy loading in list view. It works fine but I have been dealing with lots of bitmap that will be downloaded from web service. so now I get bitmap size exceeds VM budget. Increased bitmapfactory size to be 32*1024 but if it reaches 32mb again it will throw a the error. It being breaking my head for the past one week. so some one please help me out of this problem. I here by post my image loader class as well please let me know where and how should I solve this problem.

public class ImageLoader {

private HashMap<String, Bitmap> cache=new HashMap<String, Bitmap>();
Bitmap bitmap = null;

private Activity activity;
PhotosLoader photoLoaderThread=new PhotosLoader();
public ImageLoader(Activity activity){
    this.activity = activity;
    photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
    clearCache();
}
public void DisplayImage(String url, Activity activity, ImageView imageView, String[] imageUrl){
    this.activity = activity;
    if(cache.containsKey(url)){
        imageView.setImageBitmap(cache.get(url));

    }
else{
        imageView.setImageResource(R.drawable.icon);
        queuePhoto(url, activity, imageView);
    }    

}

private void queuePhoto(String url, Activity activity, ImageView imageView){
    this.activity = activity;
    photosQueue.Clean(imageView);
    PhotoToLoad p=new PhotoToLoad(url, imageView);
    synchronized(photosQueue.photosToLoad){
        photosQueue.photosToLoad.push(p);
        photosQueue.photosToLoad.notifyAll();
    }
    if(photoLoaderThread.getState()==Thread.State.NEW)
        photoLoaderThread.start();
}

public Bitmap getBitmap(String url, int sampleSize) throws Exception{
    Bitmap bm = null;
    try {
        URL request = new URL(url);
        InputStream is = (InputStream) request.getContent();
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inDither = true;
        options.inPurgeable = true;
        options.inInputShareable = true;
        options.inSampleSize = sampleSize;
        options.inTempStorage = new byte[32 * 1024];

        bm = BitmapFactory.decodeStream(is, null, options);
        if (bm!=null)
        bm = Bitmap.createScaledBitmap(bm, 115,100, true);
        is.close();
        is = null;
    } catch (IOException e) {
        throw new Exception();
    } catch (OutOfMemoryError e) {
        bm.recycle();
        bm = null;
        System.gc();
        throw new Exception();
    }
    return bm;
}
private class PhotoToLoad{
    public String url;
    public ImageView imageView;
    public PhotoToLoad(String u, ImageView i){
        url=u; 
        imageView=i;
    }
}
PhotosQueue photosQueue=new PhotosQueue();
public void stopThread(){
    photoLoaderThread.interrupt();
}
static class  PhotosQueue{
    private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();
    public void Clean(ImageView image){
        for(int j=0 ;j<photosToLoad.size();){
            try{
            if(photosToLoad.get(j).imageView==image)
                photosToLoad.remove(j);
            else
                ++j;
            }catch (Exception e) {
            }
        }
    }
}
class PhotosLoader extends Thread {
    public void run() {
        try {
            while(true){

                if(photosQueue.photosToLoad.size()==0)
                    synchronized(photosQueue.photosToLoad){
                        photosQueue.photosToLoad.wait();
                    }
                if(photosQueue.photosToLoad.size()!=0){
                    PhotoToLoad photoToLoad;
                    synchronized(photosQueue.photosToLoad){
                        photoToLoad=photosQueue.photosToLoad.pop();
                    }
                    Bitmap bmp = null;
                        bmp = getBitmap(photoToLoad.url, 1);
                    cache.put(photoToLoad.url, bmp);
                    if(((String)photoToLoad.imageView.getTag()).equals(photoToLoad.url)){
                        BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad.imageView);
                        Activity a=(Activity)photoToLoad.imageView.getContext();
                        a.runOnUiThread(bd);
                    }
                }
                if(Thread.interrupted())
                    break;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


class BitmapDisplayer implements Runnable{
    ImageView imageView;
    public  BitmapDisplayer(Bitmap b, ImageView i){
        bitmap = null;
        bitmap=b;
        imageView=i;
        }
    public void run(){
        if(bitmap!=null){
            imageView.setImageBitmap(bitmap);
            imageView.setScaleType(ScaleType.FIT_XY);
        }
        else{
            imageView.setImageResource(R.drawable.icon);
        }
    }
}

public void clearCache() {
    try{
    cache.clear();
    cache = new HashMap<String, Bitmap>();
    bitmap.recycle();   
    System.gc();
    }catch (Exception e) {
    }
}
Triad sou.
  • 2,969
  • 3
  • 23
  • 27
senthil
  • 23
  • 3
  • 1
    Looking briefly at your code, are you actually recycling all your bitmaps? I don't think you do, but only when an exception is thrown, you occasionally recycle one. Also i.e. the bmp in PhotosLoader thread are not recycled, is it? clearCache() itself is only called right in the constructor of the ImageLoader itself, is that right? – Mathias Conradt Sep 24 '11 at 10:41
  • 1
    S, i have been using this class for every list view in the project. the list view has the adapter from the apter get view method this full class will be recalled for every list so i dont know where to clear or recycle(). – senthil Sep 24 '11 at 10:55

3 Answers3

1

Quite simply you're trying to use more memory than the Android VM gives you. So you need to use less.

You either:

(1) need to manually clear your in-memory cache - i.e. once you reach a certain size or number of images

or alternatively

(2) use a HashMap of type <String, SoftReference<Bitmap>> so that the garbage collector can automatically reclaim the memory when the images are no longer being displayed.

With either of these methods your in-memory cache should not get as large as before (which is good), so you may want to save your files to disk so they do not have to be downloaded again should they be cleared from the in-memory cache.

Joseph Earl
  • 23,351
  • 11
  • 76
  • 89
  • 1
    Can u please help me to use HashMap of type > because if cahange the bitmap with soft reference it gives me null pointer exception in HashMap,get(); so can u please give me some examples to use this. thank u in advance – senthil Sep 26 '11 at 11:15
  • 2
    To put an image in the map map.put(url, new SoftReference(bitmap)); To get an image you have SoftReference ref = map.get(url); if (ref != null && ref.get() != null) { Bitmap bitmap = ref.get(); } – Joseph Earl Sep 29 '11 at 17:14
  • Thank U for your reply. i will test these methods and let u know. – senthil Sep 30 '11 at 09:23
0

Try like this..

try {
    HttpURLConnection conn= (HttpURLConnection)myFileUrl.openConnection();
    conn.setDoInput(true);
    conn.connect();
    int length = conn.getContentLength();
    InputStream is = conn.getInputStream();

    bmImg = BitmapFactory.decodeStream(is);
    proImage.setImageBitmap(bmImg); // Bind image here on UI thread

    clearBitMap(bmImg); // Call to clear cache 
} 
catch (IOException e) {
    e.printStackTrace();
}


private void clearBitMap(Bitmap bitMap) {
    bitMap = null;
    System.gc();
}
Sergio Carneiro
  • 3,726
  • 4
  • 35
  • 51
sanjay
  • 2,590
  • 17
  • 55
  • 88
0

verify below links..u can get idea about that...

http://negativeprobability.blogspot.com/2011/08/lazy-loading-of-images-in-listview.html http://stackoverflow.com/questions/5082703/android-out-of-memory-error-with-lazy-load-images http://blog.jteam.nl/2009/09/17/exploring-the-world-of-android-part-2/ http://groups.google.com/group/android-developers/browse_thread/thread/bb3c57cab27e0d91?fwc=1

sanjay
  • 2,590
  • 17
  • 55
  • 88