Image Not loaded sometime and first time using Picasso in Android

Amit Gupta
3 min readNov 10, 2021

Problem Statement:

Sometime we don’t get call back from Picasso third party library when image loading process is completed and our image Views just keep showing the placeholder and not the real image.

Sometime our animation also does not work if it is dependent on Image load completion call back from Picassso Library.

public static void setBackgroundUrl(View view, String backgroundUrl, Transformation transformation,
Callback callback, String bgColorCode, boolean toggleImageVisibility) {
if (UiUtils.INSTANCE.isColorCodeValidHex(bgColorCode)) {
view.setBackground(ResourceProvider.Companion.getInstance().getColorDrawable(bgColorCode));
return;
}
if (StringUtil.isNullOrEmpty(backgroundUrl)) {
return;
}
Target target = new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {

if (view != null) {
if(view.getWidth() > 0 && view.getHeight() > 0) {
bitmap = Bitmap.createScaledBitmap(bitmap, view.getWidth(), view.getHeight(), true);
}
view.setBackground(new BitmapDrawable(resourceProvider.getResources(), bitmap));
view.setTag(null);
if (toggleImageVisibility) {
view.setVisibility(View.VISIBLE);
}
}
if (callback != null) {
callback.onSuccess();
}
}


@Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
if (callback != null) {
callback.onError(e);
}
if (view != null && toggleImageVisibility) {
view.setVisibility(View.GONE);
}
}

@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
RequestCreator request = Picasso.get()
.load(backgroundUrl)
.config(Bitmap.Config.RGB_565);

if (transformation != null) {
request.transform(transformation);
}
request.into(target);
view.setTag(target);
}

Root Cause :

When we have many images in memory or may be many objects in memory of application then garbage collector collects all the unused or weak references to make some memory free to run other jobs.

During this process, Picasso Target class object of callback for image load status also garbage collected since these are stored as Weak reference in its internal implementation because of which your app don’t get call back and the task which need to be executed after that call back do not work.

Please find below code for Picasso internal lib for storing the object as weak reference:

Action(Picasso picasso, T target, Request request, int memoryPolicy, int networkPolicy,
int errorResId, Drawable errorDrawable, String key, Object tag, boolean noFade) {
this.picasso = picasso;
this.request = request;
this.target =
target == null ? null : new RequestWeakReference<>(this, target, picasso.referenceQueue);

this.memoryPolicy = memoryPolicy;
this.networkPolicy = networkPolicy;
this.noFade = noFade;
this.errorResId = errorResId;
this.errorDrawable = errorDrawable;
this.key = key;
this.tag = (tag != null ? tag : this);
}

Solution:

So in Picasso, once we register the call back for anonymous Target instance of Picasso to know whether bitmap is loaded completely, it is stored as a weak reference in Picasso Lib. So to avoid the collection of this target object till the moment image is completely loaded and call back is provided , we must make assign this target object reference to the strong reference object.

For this to implement , we should bind the target call back instance with the Image View by using view.setTag(target)

In this way your implementation will work in every case , even under memory pressure for your application.

If anyone do not using the Picasso target anonymous object directly then in his/her custom interface , he/she can add object of picasso target instance like:

abstract class ImageProgressCallback {
var callbackHolder: Any? =
null // required to avoid garbage collection in Image Lib (Picasso for weak ref object)
abstract fun onBitmapLoaded(bitmap: Bitmap?, from: ImageLoadFrom?)
abstract fun onBitmapFailed(e: java.lang.Exception?, errorDrawable: Drawable?)
abstract fun onPrepareLoad(placeHolderDrawable: Drawable?)
}
override fun loadImage(imageView: ImageView?, progressCallback: ImageProgressCallback?, statusCallback: ImageStatusCallback?, imageRequest: ImageRequest) {
--- some code
when {

progressCallback != null -> {
val target = ImgTarget(progressCallback)
progressCallback.callbackHolder = target
PicassoUtil.cancelRequest(target)
request.into(target)
return
}

imageView != null -> {
cancelRequest(imageView)
request.into(imageView)
return
}
}

Summary:

We should identify those objects which are required till the moment our view is visible , we should not make them weak reference and if any third party library like Picasso is using the weak reference for those object , we should bind those objects to the Strong object references.

I Hope this will help someone facing the same problem. Please clap if you find it useful.

--

--