To protect the targets from being garbage collected strong references have to be kept. One way to do it is to create field of type MutableList<Target> and store targets in it until bitmap fetch completes.

Of course, it’s really important to remove targets once they’re no longer used to not waste resources.

But how to wire this together to achieve clean, readable solution? How to know when to apply bitmap and when to ignore it? That’s where RxJava comes in handy.

ViewModel

My idea was to pass url with list of qualities into method and receive event each time image with a better quality is received. Received bitmap will be stored in LiveData observed by the view.

The viewModel will post error if event streams complete and there is no image with sufficient quality.

The code of the ImageViewModel is really simple and looks like this:

ImageFetcher

Now the fun part — the class responsible for creating observable for each image request and merging all of them together.

Two qualities

In the simplest scenario of fetching only two images simultaneously the only thing that needs to be done is to create observable for each url with quality and merging them together.

In the loadImageAndIgnoreError method I created single from instance of the class implementing SingleOnSubscribe<BitmapWithQuality> interface and then turned it into observable.

I pass Observable.empty<BitmapWithQuality>() when the error occurs so the flow of other events is not disturbed by an error in one of the observables. The error will be displayed on the view only when fetched image has no sufficient quality after all calls complete.

Multiple qualities

But what about the situation when image in more than two qualities should be fetched? For this scenario I created a solution which uses combination of map, merge and reduce operators.

First I took the list of qualities and map them into Pair(url, quality) . Then using map operator I created Observable from that pair using loadImageAndIgnoreError , just like before. As the last step I used reduce operator to merge all observables together and fetch all images simultaneously.

Reduce operator fits great in this scenario. It applies a function to each item emitted by an Observable sequentially and emit the final value. In this case the final value is the observable created from merging all component observables together.

marble diagram of reduce operator

The resulting code of loadProgressively method looks like this:

ImageFetcherSingleSubscribe

ImageFetcherSingleSubscribe is a class implementing SingleOnSubscribe<T> interface consisting of only one method: subscribe . This method receives a SingleEmitter instance that allows pushing an event in a cancellation-safe manner.

In the subscribe method I created CustomImageLoadTarget which takes an emitter, fetched image quality and unSubscribe function as a parameter. unSubscribe function must be passed so target can notify ImageFetcherSingleSubscribe that the request should be canceled and target removed from the list.

Next, I added the target into mutable list to prevent if from being garbage collected until call ends. Then the target is passed to the Picasso’s into method.

removeTargetAndCancelRequest method not only removes the target from the list but also cancels the request. The reason is that I call that function not only when image fetch completes but also when the emitter gets cancelled.

CustomImageLoadTarget

CustomImageLoadTarget is a class implementing Target interface which instance will be passed to into method.

In the init method it’s really important to call emitter.setCancellable { unSubscribe(this) } so the request is canceled and target is removed from the list after emitter was disposed.

Next part is really straightforward. When the bitmap is fetched in the onBitmapLoaded method emitter.onSuccess() is called with fetched bitmap and its quality. When fetching bitmap fails in onBitmapFailed emitter.tryOnError is called. After emitting either success or an error unSubscribe must be called to remove target from the map and release reference so it may be garbage collected.

RxJavaErrorHandler

In one of my previous posts I wrote that it’s really important to use emitter.tryOnError instead of emitter.onError to avoid getting io.reactivex.exceptions.UndeliverableException when error is emitted after observable was disposed. It’s still true and I applied that in my solution. However If you prefer, you can use emitter.onError and add custom RxJavaErrorHandler to ignore UndeliverableException .

Glide thumbnails ❤️ 🚀

Above solution might after some changes be also applied if you use Glide. However, there is other, really simple way to implement progressive image loading in Glide: thumbnails.

Thumbnails are a dynamic placeholders, that can be loaded from the Internet. Fetched thumbnail will be displayed until the actual request is loaded and processed. If the thumbnail for some reason arrives after the original image, it will be dismissed.

For most cases this solution will be sufficient but if you want to get really crazy you can even apply additional thumbnail request to the thumbnail request.

The disadvantage of this solution is that you cannot apply very complicated error or progress handling logic. Solution that I implemented using RxJava gives you more control of the current state but is also way more complicated.