I currently have a ListView with a custom adapter that gets information describing the content of the rows asynchronously. Part of each row is an image URL, that I’m planning to download asynchronously and then display.
My current plan for a strategy to download these images is:
- Keep a cache of soft references to downloaded Bitmap objects.
- When a getView() is called and the bitmap is in the cache, set the bitmap for the ImageView directly.
- If the bitmap isn’t in the cache, start loading it in a separate thread, after the download is complete add it to the cache and call notifyDataSetChanged() on the adapter.
I am also planning to kill pending downloads when the Activity object owning the ListView’s onDestroy()-method (Or possibly even in the onPause()-method) is called, but most importantly I want to kill the download of pending images when the row goes off screen. I might only actually cancel the download after a short delay, so it can be resumed without wasting bandwidth if the row comes on-screen quickly again.
I, however, am unsure about a few things:
- What is the best way to detect when a row goes off-screen so I can cancel the download?
- Is calling notifyDataSetChanged() the best thing to do after the download has completed or is there a better way?
Also any comments on the whole strategy would be appreciated.
I don’t think calling notifyDataSetChanged() is really needed… I would do it like that:
- store URL as Tag in the view when created/updated
- register a listener in downloader thread (async task???) for download keeping reference to the view and the URL
- whenever image is downloaded asynchronously, I check TAG in the view and if it matches – i would update the ImageView (important to do it in UI thread, but when using async task, it is given). The image should also be stored on SD card (and every time you request URL you should check if it is not already downloaded).
- every time when getView() reuses the view (passed view is not empty) I would check the Tag (old URL), replace it with the new URL and cancel the download of the oldURL.
I think it would be pretty much it (some corner cases might happen)…
I use the getFirstVisible and getLastVisible AdapterView properties to detect the visible rows, and put requests in a fixed size stack.
My project is open source and has a most permissive license, if you want to use it:
I found the remote resource managing / fetching in the Foursquared source code to be pretty helpful:
It caches images on disk and handles all 3 of your feature requests. See an adapter for how to use it.
As for canceling a download when a row goes off screen you’ll have to handle that yourself