2

Possible Duplicate:
Android Out of Memory error with Lazy Load images
Outofmemory error during scrolling the listview fastly

I am using listview to display the images,names from the facebook api.I can able to display images correctly.My problem is when I scroll the list fastly I am getting

    11-15 18:41:29.841: E/dalvikvm-heap(32303): 786432-byte external allocation too large for this process.
11-15 18:41:29.887: I/dalvikvm-heap(32303): Clamp target GC heap from 32.095MB to 32.000MB
11-15 18:41:29.887: E/GraphicsJNI(32303): VM won't let us allocate 786432 bytes
11-15 18:41:29.887: D/dalvikvm(32303): GC_FOR_MALLOC freed 0K, 51% free 4380K/8903K, external 23826K/25864K, paused 31ms
11-15 18:41:29.887: D/skia(32303): --- decoder->decode returned false
11-15 18:41:29.887: W/dalvikvm(32303): threadid=18: thread exiting with uncaught exception (group=0x40018560)
11-15 18:41:29.887: E/AndroidRuntime(32303): FATAL EXCEPTION: Thread-29
11-15 18:41:29.887: E/AndroidRuntime(32303): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
11-15 18:41:29.887: E/AndroidRuntime(32303):    at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
11-15 18:41:29.887: E/AndroidRuntime(32303):    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:470)
11-15 18:41:29.887: E/AndroidRuntime(32303):    at com.beerbro.utils.ImageLoader.decodeFile(ImageLoader.java:156)
11-15 18:41:29.887: E/AndroidRuntime(32303):    at com.beerbro.utils.ImageLoader.getBitmap(ImageLoader.java:99)
11-15 18:41:29.887: E/AndroidRuntime(32303):    at com.beerbro.utils.ImageLoader$PhotosLoader.run(ImageLoader.java:214)

But when I scroll this listview slowly I am not getting any errors.

Here is my Adapter class where I called Imageloader class displayImage method to convert my url into bitmap and set it to imageview.

    public class MySimpleArrayAdapter extends ArrayAdapter<String> {
    private Activity context;
    ArrayList<String> namear,msgar,idar,profimage,postimage,commentsnum,objectid,urlString;

  ImageLoader imageLoader;
  Bitmap[] bitdata;


  public MySimpleArrayAdapter(Activity c,int i,ArrayList<String> postpic, ArrayList<String> names,ArrayList<String> msg,ArrayList<String> id,ArrayList<String> proimg,Bitmap[] bit,ArrayList<String> comment,ArrayList<String> objid,ArrayList<String> web) {

      super(c, i, names);
    Log.e("adapter","adap");
    this.context = c;
    this.namear = names;
    this.msgar = msg;
    this.idar = id;
    this.profimage=proimg;
    this.postimage=postpic;
    this.bitdata=bit;
    this.commentsnum=comment;
    this.objectid=objid;
    this.urlString=web;
   this.imageLoader = new ImageLoader(context);
  }


  @Override
  public View getView(final int position, View convertView, ViewGroup parent) {
      View rowView=convertView ;
      LayoutInflater inflator =   getLayoutInflater();
      rowView = inflator.inflate(R.layout.walldata, null);

      TextView name1 = (TextView) rowView.findViewById(R.id.name);
      TextView message1 = (TextView) rowView.findViewById(R.id.msg);
      ImageView profimg= (ImageView) rowView.findViewById(R.id.profile_pic);
      ImageView postimg= (ImageView) rowView.findViewById(R.id.picpost);
  TextView comments = (TextView) rowView.findViewById(R.id.comment);

     Log.e("user",idar.get(position));


    Log.e("adapter","adap");
   name1.setText(namear.get(position));
    if(msgar.get(position)!=""){
    message1.setText(msgar.get(position));

    }
    else
    {
        message1.setVisibility(View.GONE);
    }
    if(!postimage.get(position).equals(""))
    {try{
         imageLoader.DisplayImage(postimage.get(position).replace(" ", "%20"), postimg) ;

    }
    catch(Exception e){
        e.printStackTrace();
    }
    }
    else
    {
        postimg.setVisibility(View.GONE);
    }
    try{
      imageLoader.DisplayImage(profimage.get(position).replace(" ", "%20"), profimg) ;

    }
    catch(Exception e){
       e.printStackTrace();
    }

       comments.setText(commentsnum.get(position)+"\t"+"Comments");
       comments.setOnClickListener(new OnClickListener() {


        public void onClick(View v) {
            Intent myintent=new Intent(Wall.this,Comments.class);
            myintent.putExtra("name", namear.get(position));
            myintent.putExtra("profimg", profimage.get(position));
            myintent.putExtra("message", msgar.get(position));
            myintent.putExtra("postpic", postimage.get(position));
            myintent.putExtra("objectid", objectid.get(position));
            myintent.putExtra("commentsnum",commentsnum.get(position));
            myintent.putExtra("webservice", urlString.get(position));
            startActivity(myintent);

        }
    });

    return rowView;
  }

Here is my Imageloader class

 public class ImageLoader {

    MemoryCache memoryCache=new MemoryCache();
    FileCache fileCache;
    private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());

    public ImageLoader(Context context){

        photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);

        fileCache=new FileCache(context);
    }

    public void DisplayImage(String url,ImageView imageView)
    {
        imageViews.put(imageView, url);


        Bitmap bitmap=memoryCache.get(url);

        if(bitmap!=null)
        {
            imageView.setBackgroundDrawable(new BitmapDrawable(bitmap));

            System.gc();
        }
        else
        {
            queuePhoto(url, imageView);

        }    
    }

    private Bitmap createScaledBitmap(Bitmap bitmap, int i, int j, int k) {
        return null;
    }

    private void queuePhoto(String url, ImageView imageView)
    {
        //This ImageView may be used for other images before. So there may be some old tasks in the queue. We need to discard them. 
        photosQueue.Clean(imageView);
        PhotoToLoad p=new PhotoToLoad(url, imageView);
        synchronized(photosQueue.photosToLoad){
            photosQueue.photosToLoad.push(p);
            photosQueue.photosToLoad.notifyAll();
        }

        //start thread if it's not started yet
        if(photoLoaderThread.getState()==Thread.State.NEW)
            photoLoaderThread.start();
    }

   public Bitmap getBitmap(String url) 
    {
        File f=fileCache.getFile(url);

        //from SD cache
        Bitmap b = decodeFile(f);
        if(b!=null)
            return b;

        //from web
        try {
            Bitmap bitmap=null;
            URL imageUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(30000);
            InputStream is=conn.getInputStream();
            OutputStream os = new FileOutputStream(f);
            Utils.CopyStream(is, os);
            os.close();
            bitmap = decodeFile(f);
            return bitmap;
        } catch (Exception ex){
           ex.printStackTrace();
           return null;
        }
    }//Lalit

    //decodes image and scales it to reduce memory consumption
    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);


            //Find the correct scale value. It should be the power of 2.

            final int REQUIRED_SIZE=Wall.width;
            final int REQUIRED_SIZE1=Wall.height;

            int width_tmp=o.outWidth, height_tmp=o.outHeight;
            int scale=1;

            while(true){
                if((width_tmp/2<REQUIRED_SIZE)&&(height_tmp/2<REQUIRED_SIZE1))
                    break;
                width_tmp/=2;
                height_tmp/=2;
                scale*=2;
            }

            //decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
            o2.inSampleSize=scale;
            o2.inJustDecodeBounds = false;

            return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);


        } catch (FileNotFoundException e) {}
        return null;
    }

    //Task for the queue
    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();
    }

    //stores list of photos to download
    class PhotosQueue
    {
        private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();

        //removes all instances of this ImageView
        public void Clean(ImageView image)
        {
            for(int j=0 ;j<photosToLoad.size();){
                if(photosToLoad.get(j).imageView==image)
                    photosToLoad.remove(j);
                else
                    ++j;
            }
        }
    }

    class PhotosLoader extends Thread {
        public void run() {
            try {
                while(true)
                {
                    //thread waits until there are any images to load in the queue
                    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=getBitmap(photoToLoad.url);
                        memoryCache.put(photoToLoad.url, bmp);
                        String tag=imageViews.get(photoToLoad.imageView);
                        if(tag!=null && tag.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) {
                //allow thread to exit
            }
        }
    }

    PhotosLoader photoLoaderThread=new PhotosLoader();

    //Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable
    {
        Bitmap bitmap;
        ImageView imageView;
        public BitmapDisplayer(Bitmap b, ImageView i){bitmap=b;imageView=i;}
        public void run()
        {
            if(bitmap!=null)
                imageView.setBackgroundDrawable(new BitmapDrawable(bitmap));

        }
    }

    public void clearCache() {
        memoryCache.clear();
        fileCache.clear();
    }
    public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels,int width,int height) {

        Bitmap output = Bitmap.createBitmap(width,height, Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);
        final float roundPx = pixels;

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);

        return output;
    }

I am scaling the images upto my device screen size i.e 480*854,you can find this in imageloader class,I also mention here for easy understanding

     final int REQUIRED_SIZE=Wall.width;
            final int REQUIRED_SIZE1=Wall.height;

            int width_tmp=o.outWidth, height_tmp=o.outHeight;
            int scale=1;

            while(true){
                if((width_tmp/2<REQUIRED_SIZE)&&(height_tmp/2<REQUIRED_SIZE1))
                    break;
                width_tmp/=2;
                height_tmp/=2;
                scale*=2;
            }
    }

By looking at the error I can understand that memory is not sufficient for loading bitmap,I dont know how can I deallocate memory at runtime. please help me I referred to many of the links in stackoverflow and others but couldnt fix this issue.

Community
  • 1
  • 1
veerendra
  • 523
  • 7
  • 18
  • 1
    So, the fourth time you've asked the same question, and you expect to get a different result this time? http://www.brainyquote.com/quotes/quotes/a/alberteins133991.html You were given good advice last time on what to do if you don't get good answers to your questions. Please do read it. – Simon Nov 15 '12 at 13:34
  • please take more effort in providing code – Angelo.Hannes Nov 15 '12 at 13:39
  • hello hannes,I provided the maximum code I can.please ask me if you didnt understand anything.please discuss the issue now – veerendra Nov 15 '12 at 13:45
  • this is the memory issue, it can be solved by creating the view one time and reuse it further. eg. if(convertView == null) then only creat the view. It definitely going to solve your problem. – kamal_tech_view May 24 '13 at 06:58

3 Answers3

1

there maybe two causes with your problems.first, you dont reuse the istance of the listview items that had been created. i see your code ,why you note the codes. when you scroll the listview too fast the vm will not able to create instant quick enough ,so it out of memory.and , you have better to use the buffer. the second, your bitmap is too large, when you scroll too fast ,the vm cant stand for the pressure.to resolve these ,you can reduce the size of the bitmap. hope that my answear can do something for you.

honeypig
  • 39
  • 5
  • I tried to reuse them by keeping if(convertview==null){ taking all the instances here } but it displaying prevoously loaded images first and it is taking time to load actual images.And for everly scroll I do the images are changing and when I stop it is taking time and then setting the images – veerendra Nov 15 '12 at 14:02
  • this must be the second cause, your bitmap is too large, you must handle it . make the bitmap smaller – honeypig Nov 16 '12 at 06:27
1

Well, one way around your issue that I can think of would be to use an onScrollListener, and add a flag to your adapter that can be set by the listener.

When the list is scrolling (flag = true) have the adapter skip loading the images. When the list has stopped scrolling (flag = false) have the adapter load the images. You trigger the image load by calling notifyDataSetChanged() on your adapter.

If we call the flag in the adapter isScrolling, your listener might look like this:

listView.setOnScrollListener(new OnScrollListener() {   
    public void onScroll(AbsListView view, int firstVisible,   
    int visibleCount, int total) {   
        public void onScrollStateChanged(AbsListView view, int scrollState) {   
            if (scrollState != 0)   
                listView.getAdapter()).isScrolling = true;   
            else {   
                listView.getAdapter().isScrolling = false;  
                listView.getAdapter().notifyDataSetChanged();  
            }  
        }  
    });

And in your adapter simply surround your image loading code with this:

if (!isScrolling) {
    // code to load images
}
Barak
  • 16,318
  • 9
  • 52
  • 84
0

I am not pretty sure but add these lines after initializing image component

    BitmapDrawable bd = (BitmapDrawable) imgView.getDrawable();
            if (bd != null)
            {
                bd.getBitmap().recycle();
                imgView.setImageBitmap(null);
            }
Mohd Mufiz
  • 2,236
  • 17
  • 28
  • I added bitmap.recycle() after if(bitmap!=null) { imageView.setBackgroundDrawable(new BitmapDrawable(bitmap)); System.gc(); bitmap.recycle(); // bitmap=null; // imageView.setImageBitmap(getRoundedCornerBitmap( bitmap, 10,70,70)); // imageView.setImageBitmap(bitmap); // Log.v("first", "first"); } – veerendra Nov 15 '12 at 13:35
  • hello mohd,did u have any idea regarding the issue – veerendra Nov 15 '12 at 13:45
  • do something like this ImageView profimg= (ImageView) rowView.findViewById(R.id.profile_pic); ImageView postimg= (ImageView) rowView.findViewById(R.id.picpost); after those lines add BitmapDrawable bd = (BitmapDrawable) profimg.getDrawable(); if (bd != null) { bd.getBitmap().recycle(); profimg.setImageBitmap(null); } bd= (BitmapDrawable) postimg.getDrawable(); if (bd != null) { bd.getBitmap().recycle(); postimg.setImageBitmap(null); } – Mohd Mufiz Nov 16 '12 at 05:47