Android Gallery scrolling behind the scene!
I’m absolutely sure you have ever scrolled photos on the screen. Sometimes it worked pretty well and fast, sometimes… required patience. Well, did you think of the implementation? How does it work? In the nutshell, it’s a very complicated process! It requires heavy converting tasks in the background and putting the result into a view screen. And moreover, it should be done in the most efficient way. So let’s find out how it happens.
Problem
The only thread that is responsible for drawing anything on screen for a particular application is the main or UI thread. The main thread shouldn’t be blocked. If you block it you’ll see the message: “Application not responding”. Converting from .jpg file or any other file to a thumbnail picture is a heavy task. That’s why any converting should be done outside of the main thread. So, the first thing — applying AsyncTask. The second thing, quite obvious thing for optimization is using caches. There could be done one more thing for efficiency — we can use the reused element View of the GridView. Our goal is to allow our application to be able handle any amount of images and any size.
GridView
All the elements on the screen are Views. There are different of them, we are interested in GridView and ImageView. GridView is a container for other elements in it. To fill these elements you use an adapter. Adapter provides you possibility to create view and set required information in the view. In our case it’s ImageView with a bitmap thumbnail.
AsyncTask
Android SDK provides AsyncTask as a mechanism for background tasks. There are other ways: services or using put Thread api. For each new View element of GridView we check the thumbnail image in the cache, if there is no such we should start this task. The task could be canceled if View already outside of the screen, and the task wasn’t started yet, it could be in case of fast scrolling. The task converts .jpg to thumbnail format
Note. AsyncTask was deprecated from API level 30. Google suggests to use pure java Thread mechanics, but it doesn’t change anything.
Caching
There are two caching ways: storing thumbnails in memory and storing them on the cache directory. When we require an image, first, we check in memory, and only then after we check in the cache directory. The mechanism for the cache is LRU(least recently used), i.e. we try to store in cache most usable thumbnails.
Reused View
For the most complex Views like GridView adapter provides you possibility to reuse View element in
public View getView(int position, View convertView, ViewGroup parent)
If you check the documentation it’s saying:
convertView — The old view to reuse, if possible. Note: You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view.
It’s done for more efficient way of using elements outside of the screen. It’s your obligation to check if you could reuse the same view. In our example we could reuse it, otherwise we create new one. But for both reusable and new one we should set image for the current position.
Overall mechanism
To combine it all together we have the next logic.
We are scrolling up, i.e. new elements will come up from the bottom, old elements will disappear at the top. And for new View we should provide a thumbnail. Adapter receives reused View, we are reusing it if exists. We set new ImageView, first, we check the in-memory cache. Before starting the new AsyncTask we should check the previous task associated with this view, and cancel it if it was obsolete. Then next step is made in the background thread, i.e. outside of the main thread. We check the cache, and if there is no then create a thumbnail and put it to the cache. And in the main thread, we set ImageView if it wasn’t changed, because if the task took longer and the element reused again, then we shouldn’t put an image in this View.
Conclusion
As we can see the logic is quite complex. Because we try to make it more efficient. And that’s why we use
- reuse View on GridView
- using LRU cache in memory and disk
- using asynchronous task
And as the result, we have a smooth scrolling gallery.
Sources on github.
Originally published at my blog