3

I created a ListView using an ArrayAdapter that implements the getView() method in this way:

public View getView(int position, View convertView, ViewGroup parent) {
    View rowView = convertView;
    if(rowView ==null){
    LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);         
    rowView = inflater.inflate(R.layout.rowlayout_member, parent, false);
            }
    TextView textView = (TextView) rowView.findViewById(R.id.label);
    TextView textView1 = (TextView) rowView.findViewById(R.id.label1);
    ImageView imageView = (ImageView) rowView.findViewById(R.id.icon);
    textView.setText(values[position]);
    textView1.setText(surname[position]);

    String s = values[position];
    if(foto[position] != null){
    Drawable c = Drawable.createFromPath(basePath+foto[position]);   // line 56 
    if(c!=null)
        imageView.setImageDrawable(c);
    else
        imageView.setImageResource(R.drawable.default);
    }else{
        imageView.setImageResource(R.drawable.default);                
    }
    return rowView;
}

All seems to work fine, but when I come in and go out to the List for many times(changing Activity) sometimes I obtain the following error:

08-06 15:06:14.110: E/dalvikvm-heap(3527): Out of memory on a 2380816-byte allocation.
08-06 15:06:14.110: W/dalvikvm(3527): threadid=1: thread exiting with uncaught exception (group=0x40a3d1f8)
08-06 15:06:14.120: E/AndroidRuntime(3527): FATAL EXCEPTION: main
08-06 15:06:14.120: E/AndroidRuntime(3527): java.lang.OutOfMemoryError
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:493)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:299)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:324)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.graphics.drawable.Drawable.createFromPath(Drawable.java:880)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at host.framework.component.MySimpleArrayAdapter.getView(MySimpleArrayAdapter.java:56)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.widget.AbsListView.obtainView(AbsListView.java:2012)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.widget.ListView.makeAndAddView(ListView.java:1772)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.widget.ListView.fillUp(ListView.java:705)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.widget.ListView.fillGap(ListView.java:645)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.widget.AbsListView.trackMotionScroll(AbsListView.java:4546)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.widget.AbsListView$FlingRunnable.run(AbsListView.java:3813)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.os.Handler.handleCallback(Handler.java:605)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.os.Handler.dispatchMessage(Handler.java:92)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.os.Looper.loop(Looper.java:137)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at android.app.ActivityThread.main(ActivityThread.java:4424)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at java.lang.reflect.Method.invokeNative(Native Method)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at java.lang.reflect.Method.invoke(Method.java:511)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
08-06 15:06:14.120: E/AndroidRuntime(3527):     at dalvik.system.NativeStart.main(Native Method)
08-06 15:06:14.120: W/ActivityManager(149):   Force finishing activity host.activity/.ACT_CircoliDiSupporto
08-06 15:06:14.640: W/ActivityManager(149): Activity pause timeout for ActivityRecord{413de508 host.activity/.ACT_CircoliDiSupporto}
08-06 15:06:24.140: W/ActivityManager(149): Launch timeout has expired, giving up wake lock!
08-06 15:06:24.650: W/ActivityManager(149): Activity idle timeout for ActivityRecord{41389d58 host.activity/.HostActivity}

The line 56 (highlighted in the code) seems to generate the error

Drawable c = Drawable.createFromPath(basePath+foto[position]); 
GVillani82
  • 17,196
  • 30
  • 105
  • 172
  • 1
    Are you properly releasing the objects? Do not create drawables inside getView()... at least create it only once and re use that objects later on. – AjOnFire Aug 06 '13 at 14:00
  • How can I be sure that my objects are correctly released? Uhm, maybe you are right: each time the getView is called a new Drawable is created. – GVillani82 Aug 06 '13 at 14:04

2 Answers2

3

There are a number of problems with the current implementation:

  1. The code accesses and builds a Bitmap on the main/UI thread (Drawable c = Drawable.createFromPath(basePath+foto[position]); // line 56), this would make your list scrolling quite choppy
  2. A new drawable is created each time a view is requested, on 2.x Android systems it'll take a while for an unreferenced Bitmap to be collected by the Garbage Collector (GC), exacerbating the memory issue
  3. You load the image from storage without scaling it to the proper size, this may or may not be an issue since I don't know the dimension of the image you are displaying nor the dimension of the images

I would suggest you to use Universal Image Loader library (https://github.com/nostra13/Android-Universal-Image-Loader) which would solve all of the above issues. Though I am not sure if it provides native local image loading capability or if you have to extend some class to have such a capability.


[Edit]

I wouldn't recommend rolling your own solution as it'll take more time than you'd first imagined (trust me...). But if you really want to do it you can check out this tutorial: http://codehenge.net/blog/2011/06/android-development-tutorial-asynchronous-lazy-loading-and-caching-of-listview-images/

The above tutorial solves #1 & #2, for #3 check this: Strange out of memory issue while loading an image to a Bitmap object

Community
  • 1
  • 1
Kai
  • 15,284
  • 6
  • 51
  • 82
  • Yes, you are right @Kai, my List is pretty choppy. Supposing I don't want use the Universal Loader library how can I deal with the problems you listed above? I suppose: 1) Create a separate thread that shold be interrupted if the item comes out of the scope of the list? 2) I can create a bitmap once, outside of getView? 3) I can scale all the image properly? Can this be acceptable? – GVillani82 Aug 06 '13 at 14:32
  • @Joseph82 updated answer, note that even the tutorial isn't really doing it right as it doesn't cancel the view's previous retrieve image operation, which would lead to "flashing image" artifects when scrolling very fast. So again I don't personally recommend this :P – Kai Aug 06 '13 at 14:45
  • Thank you @Kai. I think at first I'll try my solution: I like challenges :) But then I thin I will use the library that you suggest xD – GVillani82 Aug 06 '13 at 15:26
  • Good call. I was using my own AsyncTask to implement these functions, but had to fight a lot of bugs related to download-task-canceling and other issues until I realized now there are amazing image loading libs with less bugs and better functions. Certainly taught me a lesson on continuously checking the great StackOverflow for new libs/implementations/ideas. – Kai Aug 06 '13 at 15:40
  • Yes, surely it can be an opportunity to learn more about the Android world :) – GVillani82 Aug 06 '13 at 15:58
  • Well it certainly taught me how weird and different the ListView implementation is across different Android versions... For example on many version the ListView would get the 0th item three times in a roll, so you better have some very good checks in place – Kai Aug 06 '13 at 16:02
  • uhm, interesting. I was not aware about it. – GVillani82 Aug 06 '13 at 21:46
1

First of all you need a data structure like LruCache to hold the drawables that are being created.

How can I be sure that my objects are correctly released?

On activity destroy with usage the data structure, make sure the object of the class is being made null or clearing internal objects of it. (This will tell the gc that this object is ready to be cleared (in most cases)).

Also running a file operation on main thread is not the best approach as it will introduce jerkiness in listview scrolling. Try to do this in secondary thread.

AjOnFire
  • 2,868
  • 3
  • 25
  • 39