85

I am using glide for image loading in my android app, to avoid any crashes I am loading images with application context. What will be effect of this on performance of application and memory?

naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
Hari Ram
  • 3,098
  • 5
  • 23
  • 30

2 Answers2

298

What will be effect of this on performance of application and memory?

Glide provides so many .with() methods for a reason: it follows lifecycle.

Imagine a Fragment that is dynamically added to an Activity. In its onCreateView method it starts a Glide load of a 3MB image. Now, what if the user presses the back button and the Fragment is removed or the whole activity is closed?

  • If you use with(getActivity().getApplicationContext()) nothing will happen, all 3MBs of data is downloaded and then decoded, cached, probably even set to the ImageView, which is then garbage collected, because the only reference to it was from Glide internals.
  • If you use with((Fragment)this) Glide subscribes to the Fragment's lifecycle events and as soon as the Fragment is stopped, the any outstanding request should be paused; and when destroyed, all pending requests be cleared. This means that the image download will stop midway and no more resources will be used by that dead Fragment.
  • If you use with(getActivity()) Glide subscribes to the Activity's lifecycle events and the same thing happens as above, but only when the Activity is stopped or destroyed.
  • If you use with(view) Glide will do the same as in with(view.getContext()) which is equivalent to the Activity case just above.

So the best practice is to use the closest possible context/fragment to avoid unused request completions! (There's also a manual way to stop a load: Glide.clear(ImageView|Target).)


To apply this in practice try to use with(this) when possible, but when it's not, like in an adapter, or a centralized image loading method, pass in a RequestManager glide as an argument and use glide.load(..., for example:

static loadImage(RequestManager glide, String url, ImageView view) {
    glide.load(url).into(view);
}

or in adapter:

class MyAdapter extends WhichEveryOneYouUse {
    private final RequestManager glide;
    MyAdapter(RequestManager glide, ...) {
        this.glide = glide;
        ...
    }
    void getView/onBindViewHolder(... int position) {
        // ... holder magic, and get current item for position
        glide.load... or even loadImage(glide, item.url, holder.image);
    }
}

and use these from Activity/Fragment:

loadImage(Glide.with(this), url, findViewById(R.id.image));
// or
list.setAdapter(new MyAdapter(Glide.with(this), data));
TWiStErRob
  • 44,762
  • 26
  • 170
  • 254
  • 16
    Awesome explanation! It saved me a lot of time to investigate what was the main reason for often OOM exception. Thank you! – user1774316 Dec 16 '15 at 13:21
  • Could I pass in the fragment's context to an adapter as `mContext` and use `Glide.with(mContext)...` in the adapter? – terencey Apr 20 '16 at 07:30
  • @Terence Of course, but that's the same as `getActivity()` so if you replace the fragment with a transaction the resources won't be released until the user fully leaves the activity. If you need an `mContext` for other reasons (e.g. `getString`) you can just use `anyView.getContext()`. – TWiStErRob Apr 20 '16 at 08:09
  • @TWiStErRob That explains why my app memory usage can go up to 100MB at times... will be doing quite a bit of refactoring now. Thanks! – terencey Apr 20 '16 at 08:16
  • @TWiStErRob I've a question related to this but it's too verbose to be posted as a comment: http://stackoverflow.com/questions/36776086/glide-with-fragment-context – terencey Apr 21 '16 at 16:59
  • 2
    Note: calling `Glide.with(view.getContext())` is effectively equivalent to `Glide.with(this)` for any view appearing on that activity, because `RequestManagerRetriever.get(Context context)` checks whether the context is an instanceof `Activity` and casts it appropriately, e.g. `get((Activity)context)`. So it will end up using the same `get(Activity)` method either way. – Lorne Laliberte Jun 09 '16 at 23:13
  • 1
    So we should NOT need manually call `Glide.with(this).onDestroy()`? Assuming we use the correct `Context` with our `Glide.with()..` call, since `Glide` will hook into the Activity/Fragments lifecycle? – Sakiboy May 30 '17 at 08:35
  • @Sakiboy yes, no need to call that; tie your loads to the nearest possible overload of `with` and you should be ok for cleanups. – TWiStErRob May 31 '17 at 09:17
  • 1
    Is it fine to use `Glide.with(imageView).clear(imageView)` inside a Fragment -> ViewHolder where the fragment instance is not directly accessible? I'm using Glide-4.0.0-RC1. – WindRider Jul 16 '17 at 20:30
  • 1
    @WindRider not sure, try passing in a RequestManager to your adapter like show in the answer; or open a Glide issue on GitHub for a definite answer: my hunch is that it won't clear. – TWiStErRob Jul 16 '17 at 23:23
  • That's what I started to do in the first place but then discovered `with(View)`. Looking at the code both cases boil down to a `supportFragmentGet()` call. – WindRider Jul 17 '17 at 08:38
  • 2
    untill Glide developers fix "You cannot start a load for a destroyed activity" throwing exception, I suggest you to use ApplicationContext – OMArikan May 21 '18 at 16:48
  • calling Glide.with(imageView) is different from calling Glide.with(fragment), with a view, Glide picks the context of the view, hence the Activity. Whereas with the fragment, it uses the fragment lifecycle to stop and resume requests, which can be very different from the lifecycle of your activity, imagine a viewpager with a lot of fragments in one activity, when you page, Glide stops the requests for the fragment that is paged out if called with(fragment) – stephane k. Sep 12 '19 at 22:58
  • 3
    @Nurseyit Author of Coil here. Similar to Glide, Coil will use an Activity's lifecycle if you start a `load` inside of a Fragment. However, both Coil and Glide will respond to the `View.onDetach` events, which are triggered when a Fragment is moved to the backstack. Also, as Coil uses the AndroidX lifecycle components, any request made inside of a destroyed Activity will be instantly cancelled. – Colin White Sep 16 '19 at 21:57
  • what if I'm using BindingAdapter and depend on the image view to call Glide like `Glide.with(imageView)` can you help in this? – Moustafa EL-Saghier Mar 07 '22 at 15:28
  • @TWiStErRob Checked in latest version of glide 4.14.2, using Glide.with(VIEW) is working for activity destroy, fragment destroy, viewholder detach and even going in background i.e. when device home is presses. Please share if is reliable to use Glide.with(VIEW) only for all the cases – Shubham AgaRwal Feb 22 '23 at 12:52
2

A generic solution to sync Glide requests with the lifecycle of an owner. Can be called from anywhere: Activity, Fragment, RV Adapter, Custom View etc.

private fun RequestManager.syncWithLifecycleOwner(view: View): RequestManager {

val syncRequest = object : DefaultLifecycleObserver {
    override fun onStart(owner: LifecycleOwner) = onStart()
    override fun onStop(owner: LifecycleOwner) = onStop()
    override fun onDestroy(owner: LifecycleOwner) {
        onDestroy()
        owner.lifecycle.removeObserver(this)
    }
}

view.findViewTreeLifecycleOwner()?.lifecycle?.addObserver(syncRequest)

return this

}

You then can make a simple extension function like so:

fun ImageView.loadUrl(url: String) {
   Glide
      .with(context.applicationContext)
      .syncWithLifecycleOwner(this)
      .load(url)
      .into(this) 
}

findViewTreeLifecycleOwner() is present in the AndroidX Lifecycle lib. It provides the Activity or the Fragment View's lifecycle (viewLifecycleOwner) this specific ImageView is attached to. You will need to pass in application context from within the view, to make sure the Glide libs does not call the callbacks itself.

Rvb84
  • 675
  • 1
  • 6
  • 14
  • This looks interesting, but those Elvises are strange... if it doesn't find the lifecycle, it'll just do nothing and you'll never know all your images are loaded in a static context. – TWiStErRob Aug 25 '22 at 08:23
  • Omitted error logging for brevity. Up to you :) Would use Coil nowadays anyway. Fits Android lifecycles better with coroutine support. – Rvb84 Aug 25 '22 at 14:07
  • This looks to be implemented in 4.15.0 https://github.com/bumptech/glide/commit/18bba927a5e5fe7d07ada9667e9e503b9f0596a2 – TWiStErRob Feb 24 '23 at 18:56