Search code examples
androidandroid-recyclerviewpicasso

Picasso is showing wrong image when file does not exists on local storage


I've a recycler view which shows list of users with their avatar. After login I download all avatar and save them locally with the file name which is user's row id from mongo db. When the avatar file is not available in storage Picasso loads one of other user's avatar at random. What should I do prevent this. This is common code for showing or downloading image in case of unavailability

I tried using cache policies in Picasso but that didn't work either.

public ImageUtils loadFromDisk(String id, ImageView target) {
        Log.d(TAG, "loadFromDisk: imageId: " + id);
        File directory = contextWrapper.getDir("avatars", Context.MODE_PRIVATE);
        File avatarPath = new File(directory, id);
        if (avatarPath.exists()) {
            Log.d(TAG, "loadFromDisk: loaded from disk" + avatarPath);
            Picasso.get().load(avatarPath)
                    .memoryPolicy(MemoryPolicy.NO_CACHE)
                    .networkPolicy(NetworkPolicy.NO_CACHE)
                    .into(target);
        } else {
           Picasso.get().load(R.drawable.image_thumb).into(target);
        }
        return this;
    }

I call loadFromDisk from a Singleton ImageUtils class in RecyclerView bindView holder class.

PS: bindViewHolder code

public void onBindViewHolder(@NonNull RecentChatsViewHolder recentChatsViewHolder, int i) {
        Log.d(TAG, "onBindViewHolder: content row");

        if (recentChat.isNew) {
            recentChatsViewHolder.blueDot.setVisibility(View.VISIBLE);
        } else {
            recentChatsViewHolder.blueDot.setVisibility(View.GONE);
        }

        recentChatsViewHolder.avatar.setImageResource(R.drawable.ic_info_outline_white_48dp);
        ImageUtils.getInstance(context).loadFromDisk(recentChat.id, recentChatsViewHolder.avatar); //this calls above function here I don't pass the else condition of above method so there's no race condition 

    }

Solution

  • Since this is inside a recyclerView, I am assuming there's some kind of race condition happening. When you call FileDownloader to download some image. It downloads the image and the loads it to the target. But the target is not correct since the recycler view is now scrolled.

    Maybe, you could avoid this by adding id as a tag to the ImageView and check the tag once the image is downloaded. If the tag is different, don't load the image into the view. -> This would only work if the download callback/lambda passes the id.

    EDIT:

    I don't know what's happening with fallback. It would be better if you could remove it from the snippet you have added.

    I can suggest 2 things.

    1. ImageView.setImageBitmap(null) or ask Picasso to clear the target before loading a new image. If the avatar doesn't exist, clear the target.
    2. Add errorDrawable so if there's an error, it won't show the previous image.