Created
October 25, 2019 07:55
-
-
Save Anrimian/b6cfd2feb20b0abd07118d8f69d117aa to your computer and use it in GitHub Desktop.
Lightweight android image loader(with rx dependency)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class BitmapLruCache<K> extends LruCache<K, Bitmap> { | |
/** | |
* @param maxSize for caches that do not override {@link #sizeOf}, this is | |
* the maximum number of entries in the cache. For all other caches, | |
* this is the maximum sum of the sizes of the entries in this cache. | |
*/ | |
public BitmapLruCache(int maxSize) { | |
super(maxSize); | |
} | |
@Override | |
protected int sizeOf(K key, Bitmap value) { | |
return BitmapCompat.getAllocationByteCount(value); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ImageCache<T> { | |
private final BitmapLruCache<T> bitmapCache; | |
ImageCache(int maxCacheSize) { | |
bitmapCache = new BitmapLruCache<>(maxCacheSize); | |
} | |
@Nullable | |
Bitmap getBitmap(T key) { | |
return bitmapCache.get(key); | |
} | |
void putBitmap(T key, Bitmap bitmap) { | |
bitmapCache.put(key, bitmap); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class SimpleImageLoader<K, T> { | |
@DrawableRes | |
private final int loadingPlaceholder; | |
@DrawableRes | |
private final int errorPlaceholder; | |
private final int timeoutSeconds; | |
private final ImageFetcher<T> imageFetcher; | |
private final KeyFetcher<K, T> keyFetcher; | |
private final ImageCache<K> imageCache; | |
private final WeakHashMap<ImageView, Disposable> imageLoadingMap = new WeakHashMap<>(); | |
public SimpleImageLoader(int loadingPlaceholder, | |
int errorPlaceholder, | |
int timeoutSeconds, | |
int maxCacheSize, | |
ImageFetcher<T> imageFetcher, | |
KeyFetcher<K, T> keyFetcher) { | |
this.loadingPlaceholder = loadingPlaceholder; | |
this.errorPlaceholder = errorPlaceholder; | |
this.timeoutSeconds = timeoutSeconds; | |
this.imageFetcher = imageFetcher; | |
this.keyFetcher = keyFetcher; | |
imageCache = new ImageCache<>(maxCacheSize); | |
} | |
public void displayImage(@NonNull ImageView imageView, @NonNull T data) { | |
displayImage(imageView, data, errorPlaceholder); | |
} | |
public void displayImage(@NonNull ImageView imageView, | |
@NonNull T data, | |
@DrawableRes int errorPlaceholder) { | |
Bitmap bitmap = imageCache.getBitmap(keyFetcher.getKey(data)); | |
if (bitmap != null) { | |
imageView.setImageBitmap(bitmap); | |
return; | |
} | |
if (loadingPlaceholder == -1) { | |
imageView.setImageBitmap(null); | |
} else { | |
imageView.setImageResource(loadingPlaceholder); | |
} | |
Disposable disposable = imageLoadingMap.get(imageView); | |
if (disposable != null) { | |
disposable.dispose(); | |
} | |
disposable = Single.fromCallable(() -> getDataOrThrow(data)) | |
.timeout(timeoutSeconds, TimeUnit.SECONDS) | |
.subscribeOn(Schedulers.io()) | |
.observeOn(AndroidSchedulers.mainThread()) | |
.subscribe(imageView::setImageBitmap, | |
t -> imageView.setImageResource(errorPlaceholder)); | |
imageLoadingMap.put(imageView, disposable); | |
} | |
@Nullable | |
public Bitmap getImage(@Nonnull T data) { | |
return getData(data); | |
} | |
@SuppressWarnings("ResultOfMethodCallIgnored") | |
@SuppressLint("CheckResult") | |
public void displayImage(@NonNull RemoteViews widgetView, | |
@IdRes int viewId, | |
@NonNull T data, | |
@NonNull BitmapTransformer bitmapTransformer) { | |
if (loadingPlaceholder == -1) { | |
widgetView.setImageViewBitmap(viewId, null); | |
} else { | |
widgetView.setImageViewResource(viewId, loadingPlaceholder); | |
} | |
Single.fromCallable(() -> getDataOrThrow(data)) | |
.timeout(timeoutSeconds, TimeUnit.SECONDS) | |
.map(bitmapTransformer::transform) | |
.subscribe(bitmap -> widgetView.setImageViewBitmap(viewId, bitmap), | |
t -> widgetView.setImageViewResource(viewId, errorPlaceholder)); | |
} | |
@Nonnull | |
private Bitmap getDataOrThrow(T data) { | |
if (data == null) { | |
throw new RuntimeException("data is null"); | |
} | |
Bitmap bitmap = getData(data); | |
if (bitmap == null) { | |
throw new RuntimeException("bitmap is null"); | |
} | |
return bitmap; | |
} | |
@Nullable | |
private Bitmap getData(T data) { | |
K key = keyFetcher.getKey(data); | |
Bitmap bitmap = imageCache.getBitmap(key); | |
if (bitmap == null) { | |
synchronized (this) { | |
bitmap = imageCache.getBitmap(key); | |
if (bitmap == null) { | |
bitmap = imageFetcher.loadImage(data); | |
if (bitmap != null) { | |
imageCache.putBitmap(key, bitmap); | |
} | |
} | |
} | |
} | |
return bitmap; | |
} | |
public interface ImageFetcher<T> { | |
Bitmap loadImage(T data); | |
} | |
public interface KeyFetcher<K, T> { | |
K getKey(T data); | |
} | |
public interface BitmapTransformer { | |
Bitmap transform(Bitmap bitmap); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open class SimpleImageLoader<K, T>(@field:DrawableRes | |
private val loadingPlaceholder: Int, | |
@field:DrawableRes | |
private val errorPlaceholder: Int, | |
private val timeoutSeconds: Long, | |
maxCacheSize: Int, | |
private val imageFetcher: (T) -> Bitmap, | |
private val keyFetcher: (T) -> K) { | |
private val imageCache: ImageCache<K> = ImageCache(maxCacheSize) | |
private val imageLoadingMap = WeakHashMap<ImageView, Disposable>() | |
@JvmOverloads | |
fun displayImage(imageView: ImageView, | |
data: T, | |
@DrawableRes errorPlaceholder: Int = this.errorPlaceholder) { | |
val bitmap = imageCache.getBitmap(keyFetcher(data)) | |
if (bitmap != null) { | |
imageView.setImageBitmap(bitmap) | |
return | |
} | |
if (loadingPlaceholder == -1) { | |
imageView.setImageBitmap(null) | |
} else { | |
imageView.setImageResource(loadingPlaceholder) | |
} | |
var disposable = imageLoadingMap[imageView] | |
disposable?.dispose() | |
disposable = Single.fromCallable { getDataOrThrow(data) } | |
.timeout(timeoutSeconds, TimeUnit.SECONDS) | |
.subscribeOn(Schedulers.io()) | |
.observeOn(AndroidSchedulers.mainThread()) | |
.subscribe(imageView::setImageBitmap | |
) { imageView.setImageResource(errorPlaceholder) } | |
imageLoadingMap[imageView] = disposable | |
} | |
fun getImage(element: T): Bitmap? { | |
return getData(element) | |
} | |
@SuppressLint("CheckResult") | |
fun displayImage(widgetView: RemoteViews, | |
@IdRes viewId: Int, | |
data: T, | |
bitmapTransformer: (Bitmap) -> Bitmap) { | |
if (loadingPlaceholder == -1) { | |
widgetView.setImageViewBitmap(viewId, null) | |
} else { | |
widgetView.setImageViewResource(viewId, loadingPlaceholder) | |
} | |
Single.fromCallable { getDataOrThrow(data) } | |
.timeout(timeoutSeconds, TimeUnit.SECONDS) | |
.map(bitmapTransformer::invoke) | |
.subscribe({ bitmap -> widgetView.setImageViewBitmap(viewId, bitmap) }, | |
{ widgetView.setImageViewResource(viewId, errorPlaceholder) }) | |
} | |
private fun getDataOrThrow(data: T?): Bitmap { | |
if (data == null) { | |
throw RuntimeException("data is null") | |
} | |
return getData(data) ?: throw RuntimeException("bitmap is null") | |
} | |
private fun getData(data: T): Bitmap? { | |
val key = keyFetcher(data) | |
var bitmap = imageCache.getBitmap(key) | |
if (bitmap == null) { | |
synchronized(this) { | |
bitmap = imageCache.getBitmap(key) | |
if (bitmap == null) { | |
bitmap = imageFetcher(data) | |
if (bitmap != null) { | |
imageCache.putBitmap(key, bitmap) | |
} | |
} | |
} | |
} | |
return bitmap | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment