A simple task such as displaying your gallery photos in a ListView component can lead to a significant loss of performance in your Android application, to the point where it would render the application
unusable. Worse yet, the application might run out of memory and crash while loading bitmaps. Just think about user quickly scrolling through a list of hundreds of photos each the size of 3-10 megabytes.
Thus, to display pictures in ListView is not straightforward and requires several performance considerations.
It doesn’t make sense for us to keep default ListView behavior which continuously frees up and loads our bitmaps when our users scroll back and forth through the list.
Caching allows us to store bitmaps in memory and quickly retrieve them on consecutive views while scrolling. Thankfully, Android provides us with
LRUCache that helps us cache our bitmaps. Thus, we can create a fairly simple class for our LRUCache.
The above code allows creation of LruCache with a quarter of available memory, and adding and retrieving bitmaps by key value, which is a position of an image in our ListView.
Loading hundreds of full-sized image bitmaps would quickly cause our application to crash from lack of memory. Oftentimes, displaying image at fraction of the original size in a list is sufficient.
The functions below help us resize the image and resample the bitmap before using it further.
Basically, calculateInSampleSize takes original image width or height and divides them by the target width or height. Returned inSampleSize then gives us a divisor to create processed bitmaps that are a
fraction of the original size. Finally, decodeSampledBitmapFromString reads our bitmap from provided image path and returns resized smaller bitmap.
Initially, we want to load all our bitmaps in a separate thread or process, so we do not freeze our UI thread. To achieve that, we can use
AsyncTask. In our case, this is also where we want to re-sample our bitmaps and place the images into cache. Therefore,
our AsyncTask would look like this:
Putting it all together
In the main activity, we would initialize our bitmap cache in onCreate() method:
Then, in our ListView adapter, we would add loadBitmap() function to either set ImageViews bitmaps from cache or to start a new AsyncTask.
Also, we could also do other things to optimize performance, such as implementing ViewHolder Pattern to recycle views
in a static class. In our example, ViewHolder will hold two things: ImageView and its position in ListView.
Finally, Android has a limit of 128 simultaneous AsyncTasks and gives an error if we exceed that limit. This error is entirely possible when loading a thousand bitmaps in a ListView.
One way to prevent the error from happening and to manage memory is to implement a static class to keep and control current AsyncTask count.
Our AsyncTask class could then increment the count in the AsyncTask constructor and decrement in onPostExecute() method. We would check the current count against arbitrary maximum
prior to launching new ImageLoaderTask(...) in List Adapter loadBitmap(...) method.