How it can be done? Image downloading on pure threads.

Andrei Belous
4 min readMar 7, 2021

--

Hey, as 2021 developers we have a lot of options to execute simple tasks. Hundreds of amazing open source libraries are available on GitHub, allowing us as developers write less code.

But there is a downside of it, very often developers (such as myself) do not understand how these libraries are working internally.

This article goal is to show how such simple procedure as image downloading into ImageView can be done using only Android SDK and pure Threads (once i was asked exact same question on one of interview). So let’s start.

As an example we will implement simple app that has one screen with list of images and theirs titles.

Here is the final result of our work

1. Let’s determine API for our download service. It seems like for the first implementation we will need only target ImageView and image url itself

our API version of image downloader

2. On android we cannot execute network requests on main thread, so we will need to execute our image downloading part in some other thread. But in case of images inside of recycler view we might need to download multiple images in parallel, so we will need to somehow manage all this threads created by ourselves to reuse them when image downloading is finished. Fortunately JDK already have similar mechanism called ExecutorService. ExecutorService allows us to execute Runnables and internally it manages all thread creation and reusing for us.

As we don’t know exact amount of images need to be downloaded simultaneously best choice will be CachedThreadPool Executor Service, as it creates new thread as needed, and reuses threads that already finished its work, which can be created using factory method of Executors class.

we added executor service to our image downloader

3. Then we need to download image from URL. In this case JDK also rescues us offering HttpURLConnection class. When we establish connection to specific URL, we can get an input stream and convert it to bitmap. So our image downloading part will look like this.

image downloading and then converting it to bitmap

But actually we don’t need a full downloaded bitmap, because on screen we are showing only thumbnails.

we added width and height (which we later will take from image view) to scale bitmap

4. Using this function we finally can write our Runnable

our first version image downloading runnable

So… let’s try to execute this one, and see the result

first build of our image download

And it’s working… kinda… images for some reason randomly changing… let’s try to fix a few issues.

First of all we need to remember that android views are not thread safe. And better option is not to set bitmap directly from download thread, but to use Handler. What Handler does is just adds messages to MessageQueue of associated Looper, and then executes them. Each view already has it’s own handler, so let’s use that.

we are using internal view handler to process loaded bitmap

Another thing we need to cover is case when we don’t need to set bitmap into image view anymore. For example if we scrolled out cell with image out of the screen. For this case android provides us with OnAttachStateChangeListener which tells us when view is attached/detached from window. And also we need to refresh current bitmap (if loaded).

And last thing, we can implement simple caching of loaded bitmaps not to load them again. As we downloading them in different threads let’s use ConcurrentHashMap for it.

we added ConcurrentHashMap to save loaded bitmaps
we added cache as ConcurrentHashMap to save already loaded bitmaps

And voila!

final result

Of course, this is not a perfect solution, but one of the many approaches to solve this problem. If you have something to add to this, pull request will be highly appriciated. All code is available on GitHub. Hope you enjoyed reading it. Cheers!

--

--

No responses yet