[android-developers] Re: Lazy Loading Images in a ListView
I haven't read through the whole thread, but FWIW there's a very good introduction to list view 'lazy loading' techniques in Beginning Android: http://www.apress.com/book/view/1430224193. Look in the sample code under fancylists -- well documented in the book. Sam Dutton On Dec 8 2009, 2:34 pm, Samuh samuh.va...@gmail.com wrote: I trying a hand at ListViews and my current experiment is aimed at displaying some data in a ListView. The data to be displayed in each row is simple: an image and some text. The images come from a remote server and the textual data is hardcoded. I have a class that downloads images using AsyncTask and caches the list of images fetched as SoftReferences in a LinkedHashMap. I am also passing a reference of the view to this class, so when the image download/cache read is complete the class will set appropriate Bitmap in the view. Following is my code: //class BasicAdapter extends ArrayAdapterRowData ... static class ViewHolder{ TextView text; ImageView icon; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if(convertView == null){ convertView = mInflater.inflate(R.layout.listview_row,null); holder = new ViewHolder(); holder.text = (TextView) convertView.findViewById(R.id.row_txt); holder.icon = (ImageView) convertView.findViewById(R.id.row_img); convertView.setTag(holder); } else{ holder = (ViewHolder) convertView.getTag(); } holder.text.setText(mDataSet.get(position).getMText()); //set default icon holder.icon.setImageResource(R.drawable.icon); // set the actual image from the cache String imageUrl = mDataSet.get(position).getMUrl(); WriteThroughCache.getImage(holder.icon, imageUrl); return convertView; } // My helper class that manages cache and image Download public class WriteThroughCache{ static LinkedHashMapString,SoftReferenceBitmap imageCache = new LinkedHashMapString,SoftReferenceBitmap(); public static void getImage(ImageView icon, String imageUrl){ if(imageCache.containsKey(imageUrl)){ Bitmap image = imageCache.get(imageUrl).get(); if(image != null){ icon.setImageBitmap(image); return; } } new LoadImageInBackground().execute(new Object[]{icon,imageUrl}); } static class LoadImageInBackground extends AsyncTaskObject, Void, Bitmap{ ImageView mIcon = null; String mUrl = null; @Override protected Bitmap doInBackground(Object... params) { mIcon = (ImageView)params[0]; mUrl = (String) params[1]; Bitmap image = BitmapFactory.decodeStream (GetMethodExecutor.getResponseStream(mUrl)); return image; } @Override protected void onPostExecute(Bitmap result) { super.onPostExecute(result); mIcon.setImageBitmap(result); imageCache.put(mUrl, new SoftReferenceBitmap(result)); } } } Problem: The applications seems to work just fine. However, when I touch scroll the screen sometimes, the rows display the default image and Loading.. text. These are never refreshed. But if I fling the listview, everything works normal then. I am not sure of what I am missing here, please guide. Thanks. -- You received this message because you are subscribed to the Google Groups Android Developers group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en
Re: [android-developers] Re: Lazy Loading Images in a ListView
Sam Dutton wrote: I haven't read through the whole thread, but FWIW there's a very good introduction to list view 'lazy loading' techniques in Beginning Android: http://www.apress.com/book/view/1430224193. Look in the sample code under fancylists -- well documented in the book. Hm... I wrote that book, and I'm pretty sure I didn't cover the OP's question there. It's related to the Fancy ListViews chapter, but adding asynchronous thumbnail loading is a whole 'nuther kettle of fish. Fortunately for the OP, I have already filleted, breaded, and seasoned that kettle of fish. Here's a reusable component that offers asynchronous thumbnail downloading and application to a ListView: http://github.com/commonsguy/cwac-thumbnail -- Mark Murphy (a Commons Guy) http://commonsware.com | http://twitter.com/commonsguy _The Busy Coder's Guide to *Advanced* Android Development_ Version 1.3 Available! -- You received this message because you are subscribed to the Google Groups Android Developers group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en
[android-developers] Re: Lazy Loading Images in a ListView
skyhigh: thanks for your reply! I haven't tried to do a lazy background image load like you are doing, but have done some code that uses a holder/wrapper to store the layout findViewById which is a nice optimization so that it doesn't have to call inflate and findViewById again when each row is displayed. My understanding is that when you do this it will allocate enough views for each of the rows that are visible on the screen, and then as a row scrolls off the screen, it will reuse the already initialized view for a new row that is scrolling onto the screen. As you can see, I have used a view holder pattern too; as was explained in the Google I/O conference by Romain. In looking at your code I think you may have a problem because of the way that the views for each row get reused when they disappear from sight. The same view that was used for a row that just disappeared can be reused for a new row that is appearing as the list scrolls. Your getView routine is handling this by detecting that the convertView is non-null when it has already been filled in when this view was used by a previous row. However you don't have any mechanism to detect when a view has been reused and is now displaying a different row and the URL has changed to a different URL while the background AsyncTask was retrieving the image. By the time the AsyncTask finishes retrieving the image, the view that it saved the reference to, in order to set the image, could have been reused to display a different row in the list. If you can add code to check if it is still the expected URL prior to setting the image I think it might solve your problem. If the URL has changed then skip setting the lazy image on that view, and just add the image to your cache. Thanks for pointing this out; I was able to recreate the situation that you've described by inserting a delay in the doInBackground() method of AsyncTask. Result: When I scrolled a few dozen rows and stopped, the icons in the new Frame displayed the older icons first and then the list was refreshed. I could probably work around this by: 1. Tag the view with some unique Id and when setting the image in the AsyncTask, find the View by Tag, and then proceed with setImage(..). 2. I think, I could optimize the implementation further by not loading the image when the list is scrolling; similar to what SlowAdapter does. -- You received this message because you are subscribed to the Google Groups Android Developers group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en
[android-developers] Re: Lazy Loading Images in a ListView
On Dec 9, 1:07 pm, Romain Guy romain...@android.com wrote: You can check out the example I wrote at code.google.com/p/shelves. It does lazy loading of images from the sdcard. Thanks Romain for your time; I will surely check the implementation out. Cheers! -- You received this message because you are subscribed to the Google Groups Android Developers group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en
[android-developers] Re: Lazy Loading Images in a ListView
I have also recently been implementing adapter-based image loading into a list. I found that when I used SoftReferences, they started getting cleared very quickly indeed, after my cache of thumbnails was up to maybe 20 or so. When I use normal references, the cache grows happily to a couple of hundred thumbnails without OOMs being thrown. Being a java newbie, I don't pretend to understand this, but I went with normal references and careful OOM catching. BTW, tip maybe, I used onScroll and onScrollStateChanged from the list to initiate cache fills (rather than filling as a result of calls from getView()) because otherwise I found scrolling getting sluggish as the image cache got busy filling the cache while scrolling was still ongoing. Lee On Dec 10, 2:21 pm, Samuh samuh.va...@gmail.com wrote: On Dec 9, 1:07 pm, Romain Guy romain...@android.com wrote: You can check out the example I wrote at code.google.com/p/shelves. It does lazy loading of images from the sdcard. Thanks Romain for your time; I will surely check the implementation out. Cheers! -- You received this message because you are subscribed to the Google Groups Android Developers group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en
[android-developers] Re: Lazy Loading Images in a ListView
Oh nevermind, I saw you realised the scroll problem already :-) Lee -- You received this message because you are subscribed to the Google Groups Android Developers group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en
[android-developers] Re: Lazy Loading Images in a ListView
I found that when I used SoftReferences, they started getting cleared very quickly indeed, after my cache of thumbnails was up to maybe 20 or so. When I use normal references, the cache grows happily to a couple of hundred thumbnails without OOMs being thrown. Being a java newbie, I don't pretend to understand this, but I went with normal references and careful OOM catching. One of the possible reasons, I can think of, why Romain chose to persist the fetched images on SDcard(in his Shelves app) is because when caching is done on heap, the objects keep getting garbage collected so you have to repeat the time consuming fetches. -- You received this message because you are subscribed to the Google Groups Android Developers group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en
Re: [android-developers] Re: Lazy Loading Images in a ListView
You can check out the example I wrote at code.google.com/p/shelves. It does lazy loading of images from the sdcard. On Tue, Dec 8, 2009 at 11:45 PM, skyhigh skyhigh1...@gmail.com wrote: I haven't tried to do a lazy background image load like you are doing, but have done some code that uses a holder/wrapper to store the layout findViewById which is a nice optimization so that it doesn't have to call inflate and findViewById again when each row is displayed. My understanding is that when you do this it will allocate enough views for each of the rows that are visible on the screen, and then as a row scrolls off the screen, it will reuse the already initialized view for a new row that is scrolling onto the screen. In looking at your code I think you may have a problem because of the way that the views for each row get reused when they disappear from sight. The same view that was used for a row that just disappeared can be reused for a new row that is appearing as the list scrolls. Your getView routine is handling this by detecting that the convertView is non-null when it has already been filled in when this view was used by a previous row. However you don't have any mechanism to detect when a view has been reused and is now displaying a different row and the URL has changed to a different URL while the background AsyncTask was retrieving the image. By the time the AsyncTask finishes retrieving the image, the view that it saved the reference to, in order to set the image, could have been reused to display a different row in the list. If you can add code to check if it is still the expected URL prior to setting the image I think it might solve your problem. If the URL has changed then skip setting the lazy image on that view, and just add the image to your cache. -- You received this message because you are subscribed to the Google Groups Android Developers group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en -- Romain Guy Android framework engineer romain...@android.com Note: please don't send private questions to me, as I don't have time to provide private support. All such questions should be posted on public forums, where I and others can see and answer them -- You received this message because you are subscribed to the Google Groups Android Developers group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en
[android-developers] Re: Lazy Loading Images in a ListView
I haven't tried to do a lazy background image load like you are doing, but have done some code that uses a holder/wrapper to store the layout findViewById which is a nice optimization so that it doesn't have to call inflate and findViewById again when each row is displayed. My understanding is that when you do this it will allocate enough views for each of the rows that are visible on the screen, and then as a row scrolls off the screen, it will reuse the already initialized view for a new row that is scrolling onto the screen. In looking at your code I think you may have a problem because of the way that the views for each row get reused when they disappear from sight. The same view that was used for a row that just disappeared can be reused for a new row that is appearing as the list scrolls. Your getView routine is handling this by detecting that the convertView is non-null when it has already been filled in when this view was used by a previous row. However you don't have any mechanism to detect when a view has been reused and is now displaying a different row and the URL has changed to a different URL while the background AsyncTask was retrieving the image. By the time the AsyncTask finishes retrieving the image, the view that it saved the reference to, in order to set the image, could have been reused to display a different row in the list. If you can add code to check if it is still the expected URL prior to setting the image I think it might solve your problem. If the URL has changed then skip setting the lazy image on that view, and just add the image to your cache. -- You received this message because you are subscribed to the Google Groups Android Developers group. To post to this group, send email to android-developers@googlegroups.com To unsubscribe from this group, send email to android-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/android-developers?hl=en