@Override
            public void subscribe(final ObservableEmitter<ImageData> emitter) {
                // Canceling disposable must sever this connection
                final CancellableDisposable aborter = new CancellableDisposable(emitter::onComplete);
                disposable.add(aborter);
                // Canceling this subscription must dispose the data retrieval
                emitter.setDisposable(AndroidRxUtils.computationScheduler.scheduleDirect(() -> {
                    delayForTest();
                    final InternalImageData loaded = loadFromDisk();
                    final BitmapDrawable bitmap = loaded.bitmapDrawable;
                    if (loaded.isFresh) {
                        if (!onlySave) {
                            emitter.onNext(new ImageData(bitmap, loaded.metadata, loaded.localUri));
                        }
                        emitter.onComplete();
                        return;
                    }
                    if (bitmap != null && !onlySave) {
                        emitter.onNext(new ImageData(bitmap, loaded.metadata, loaded.localUri));
                    }
                    AndroidRxUtils.networkScheduler.scheduleDirect(() -> downloadAndSave(emitter, aborter));
                }));
            }
Caches are loaded from disk on a computation scheduler to avoid using more threads than cores while decoding
the image. Downloads happen on downloadScheduler, in parallel with image decoding.
main/src/main/java/cgeo/geocaching/network/HtmlImage.java