Glide: How do I save loaded image to a specific directory?

Created on 22 Jun 2016  路  3Comments  路  Source: bumptech/glide

I have a recyclerView that loads a bunch of images (thumbnail) from a server. When the user taps the image, I want it to show the full image size instantly without making another network request. How do I accomplish this using Glide? I'm thinking of saving the loaded image to a specific directory and when the user taps the image I'll simply load the image from that directory.

I'm not sure if this is the best approach however. Any ideas?

question

Most helpful comment

Yep, that's correct. When you say instantly you mean in 0ms? Because even though the file is on disk there's some time involved in loading it from disk. If you want ~0ms you need to hit the memory cache. For that you need to fire another load on each bind of list:

public void loadImage(final MainChatImgModel models, final ImageView imageView, final ProgressBarCircularIndeterminate progressBar, final RelativeLayout loadingPanel){
    final String photoPath = models.getData().get(0).getSrc();
    glide.load(photoPath)
         .diskCacheStrategy(DiskCacheStrategy.ALL)
         .listener(new ProgressVisibilityManipulator(progressBar, loadingPanel))
         .into(imageView);

    // could be calculated based on screen size and orientation for example; this is portrait
    final int predictedFullWidth = 1080;
    final int predictedFullHeight = 1920;

    imageView.setOnClickListener(new View.OnClickListener() {
        @Override public void onClick(View view) {
            showFullImage((Activity)imageView.getContext(), photoPath, predictedFullWidth, predictedFullHeight);
        }
    });
    glide
         .load(photoPath) // (1)
         .diskCacheStrategy(DiskCacheStrategy.SOURCE)
         .priority(Priority.LOW)
         .fitCenter() // (2)
         .preload(predictedFullWidth, predictedFullHeight); // (3)
}
private static void showFullImage(Activity activity, String src, int width, int height) {
    Intent intent = new Intent(activity, PhotoDetailActivity.class);
    intent.putExtra(PhotoDetailActivity.KEY_PATH, src);
    intent.putExtra(PhotoDetailActivity.KEY_WIDTH, width);
    intent.putExtra(PhotoDetailActivity.KEY_HEIGHT, height);
    activity.startActivity(intent);
    activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}
private static class ProgressVisibilityManipulator implements RequestListener<String, GlideDrawable> {
    private final ProgressBarCircularIndeterminate progressBar;
    private final RelativeLayout loadingPanel;
    public ProgressVisibilityManipulator(ProgressBarCircularIndeterminate progressBar, RelativeLayout loadingPanel) {
        this.progressBar = progressBar;
        this.loadingPanel = loadingPanel;
        progressBar.setVisibility(View.VISIBLE);
        loadingPanel.setVisibility(View.VISIBLE);
    }
    @Override public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
        progressBar.setVisibility(View.GONE);
        loadingPanel.setVisibility(View.GONE);
        return false;
    }
    @Override public boolean onResourceReady(GlideDrawable resource, final String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
        progressBar.setVisibility(View.GONE);
        loadingPanel.setVisibility(View.GONE);
        return false;
    }
}

and in the activity:

public class PhotoDetailActivity extends ?Activity {
    public static final String KEY_PATH = "photoPath";
    public static final String KEY_WIDTH = "photoWidth";
    public static final String KEY_HEIGHT = "photoHeight";
    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_photo_detail);
        iv_photo = (ImageView) findViewById(R.id.iv_photo_detail);
        loadingPanel = (RelativeLayout)findViewById(R.id.loadingPanel);
        String photoPath = getIntent().getStringExtra(KEY_PATH);
        int width = getIntent().getIntExtra(KEY_WIDTH, Target.SIZE_ORIGINAL);
        int height = getIntent().getIntExtra(KEY_HEIGHT, Target.SIZE_ORIGINAL);
        Glide.with(this)
             .load(photoPath) // (1)
             .diskCacheStrategy(DiskCacheStrategy.SOURCE)
             .fitCenter() // (2)
             .override(width, height) // (3)
             .into(iv_photo);
    }
}

(1), (2), (3) has to match in order to hit the memory cache. I didn't test this so let me know if it worked. As far as I know starting two loads for the same image will download it only once. Add a listener to the activity's load to see if it's loaded from memory cache.

All 3 comments

Load the thumbnail using .diskCacheStrategy(ALL), then on user tap load the full image using .diskCacheStrategy(SOURCE). This way there'll be two copies saved in Glide cache. One for a small resized/cropped/fitted image for fast reloading of thumbs on scrolling and another the original file. The actual download will happen when the thumbnail is shown.

I can't notice any difference in speed doing it the way you said. Could you check my code and see what's wrong with it ?

code from my recyclerview: https://gist.github.com/piedpiperlol/47d5de214f4f9ba9e4aa1d9fdcee1613

    public void loadImage(final MainChatImgModel models, final ImageView imageView, final ProgressBarCircularIndeterminate progressBar, final RelativeLayout loadingPanel){
        progressBar.setVisibility(View.VISIBLE);
        loadingPanel.setVisibility(View.VISIBLE);
        glide.load(models.getData().get(0).getSrc()).diskCacheStrategy(DiskCacheStrategy.ALL).listener(new RequestListener<String, GlideDrawable>() {
            @Override
            public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
                return false;
            }
            @Override
            public boolean onResourceReady(GlideDrawable resource, final String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
                progressBar.setVisibility(View.GONE);
                loadingPanel.setVisibility(View.GONE);
                imageView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        showFullImage(imageView,models.getData().get(0).getSrc());
                    }
                });
                return false;
            }
        }).into(imageView);
    }

    private void showFullImage(ImageView imageView,String src){
        Intent intent = new Intent(imageView.getContext(), PhotoDetailActivity.class);
        intent.putExtra("photoPath",src);
        Activity activity = (Activity)imageView.getContext();
        activity.startActivity(intent);
        activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
    }

this is photodetailactivity, which is opened after the user taps the image
https://gist.github.com/piedpiperlol/7e7a076314e7c33c3471add0a2be9b03

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_photo_detail);
        iv_photo = (ImageView) findViewById(R.id.iv_photo_detail);
        loadingPanel = (RelativeLayout)findViewById(R.id.loadingPanel);
        String photoPath = getIntent().getStringExtra("photoPath");
        Glide.with(this).load(photoPath).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(iv_photo);
  }

Yep, that's correct. When you say instantly you mean in 0ms? Because even though the file is on disk there's some time involved in loading it from disk. If you want ~0ms you need to hit the memory cache. For that you need to fire another load on each bind of list:

public void loadImage(final MainChatImgModel models, final ImageView imageView, final ProgressBarCircularIndeterminate progressBar, final RelativeLayout loadingPanel){
    final String photoPath = models.getData().get(0).getSrc();
    glide.load(photoPath)
         .diskCacheStrategy(DiskCacheStrategy.ALL)
         .listener(new ProgressVisibilityManipulator(progressBar, loadingPanel))
         .into(imageView);

    // could be calculated based on screen size and orientation for example; this is portrait
    final int predictedFullWidth = 1080;
    final int predictedFullHeight = 1920;

    imageView.setOnClickListener(new View.OnClickListener() {
        @Override public void onClick(View view) {
            showFullImage((Activity)imageView.getContext(), photoPath, predictedFullWidth, predictedFullHeight);
        }
    });
    glide
         .load(photoPath) // (1)
         .diskCacheStrategy(DiskCacheStrategy.SOURCE)
         .priority(Priority.LOW)
         .fitCenter() // (2)
         .preload(predictedFullWidth, predictedFullHeight); // (3)
}
private static void showFullImage(Activity activity, String src, int width, int height) {
    Intent intent = new Intent(activity, PhotoDetailActivity.class);
    intent.putExtra(PhotoDetailActivity.KEY_PATH, src);
    intent.putExtra(PhotoDetailActivity.KEY_WIDTH, width);
    intent.putExtra(PhotoDetailActivity.KEY_HEIGHT, height);
    activity.startActivity(intent);
    activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}
private static class ProgressVisibilityManipulator implements RequestListener<String, GlideDrawable> {
    private final ProgressBarCircularIndeterminate progressBar;
    private final RelativeLayout loadingPanel;
    public ProgressVisibilityManipulator(ProgressBarCircularIndeterminate progressBar, RelativeLayout loadingPanel) {
        this.progressBar = progressBar;
        this.loadingPanel = loadingPanel;
        progressBar.setVisibility(View.VISIBLE);
        loadingPanel.setVisibility(View.VISIBLE);
    }
    @Override public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
        progressBar.setVisibility(View.GONE);
        loadingPanel.setVisibility(View.GONE);
        return false;
    }
    @Override public boolean onResourceReady(GlideDrawable resource, final String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
        progressBar.setVisibility(View.GONE);
        loadingPanel.setVisibility(View.GONE);
        return false;
    }
}

and in the activity:

public class PhotoDetailActivity extends ?Activity {
    public static final String KEY_PATH = "photoPath";
    public static final String KEY_WIDTH = "photoWidth";
    public static final String KEY_HEIGHT = "photoHeight";
    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_photo_detail);
        iv_photo = (ImageView) findViewById(R.id.iv_photo_detail);
        loadingPanel = (RelativeLayout)findViewById(R.id.loadingPanel);
        String photoPath = getIntent().getStringExtra(KEY_PATH);
        int width = getIntent().getIntExtra(KEY_WIDTH, Target.SIZE_ORIGINAL);
        int height = getIntent().getIntExtra(KEY_HEIGHT, Target.SIZE_ORIGINAL);
        Glide.with(this)
             .load(photoPath) // (1)
             .diskCacheStrategy(DiskCacheStrategy.SOURCE)
             .fitCenter() // (2)
             .override(width, height) // (3)
             .into(iv_photo);
    }
}

(1), (2), (3) has to match in order to hit the memory cache. I didn't test this so let me know if it worked. As far as I know starting two loads for the same image will download it only once. Add a listener to the activity's load to see if it's loaded from memory cache.

Was this page helpful?
0 / 5 - 0 ratings