glide icon indicating copy to clipboard operation
glide copied to clipboard

Glide.with throws unnecessary and undocumented exception

Open nikclayton opened this issue 8 months ago • 3 comments

In Glide 4.16.0 I'm trying to cancel a load in response to a callback from a third party library.

The third party library is tracking the view lifecycle, and when the view is detached calls my callback to cancel any outstanding image loads. The view might be detached as part of the normal view lifecycle when it's a view managed by an adapter associated with a RecyclerView and the user is scrolling, or it might be detached because the activity is ending.

The function (i.e., my code) it's calling looks like this:

    override fun cancel(imageView: ImageView) {
        Glide.with(imageView).clear(imageView)
    }

That throws an undocumented IllegalArgumentException because Glide.with eventually calls this in RequestManagerRetriever.java:

  @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
  private static void assertNotDestroyed(@NonNull Activity activity) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
      throw new IllegalArgumentException("You cannot start a load for a destroyed activity");
    }
  }

This is misleading, as I am not trying to "start a load for a destroyed activity". At worst, calling .clear(imageView) when the activity is destroyed should be a no-op.

Please document this exception. Please also consider pushing this assert to the .load() methods, and/or only asserting in debug builds.

nikclayton avatar Jun 01 '25 16:06 nikclayton

Thanks for reporting this, @nikclayton . I think we can document this, but can you also include the stack trace that you are observing? That would be helpful in terms of mitigating this issue.

falhassen avatar Jun 03 '25 17:06 falhassen

Exception java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity
  at com.bumptech.glide.manager.RequestManagerRetriever.assertNotDestroyed (RequestManagerRetriever.java:236)
  at com.bumptech.glide.manager.RequestManagerRetriever.get (RequestManagerRetriever.java:110)
  at com.bumptech.glide.manager.RequestManagerRetriever.get (RequestManagerRetriever.java:176)
  at com.bumptech.glide.Glide.with (Glide.java:634)
  at app.pachli.MainDrawerImageLoader.cancel (MainActivity.kt:1312)
  at com.mikepenz.materialdrawer.model.BaseDescribeableDrawerItem.bindViewHelper (BaseDescribeableDrawerItem.kt:56)
  at com.mikepenz.materialdrawer.util.DrawerImageLoader.cancelImage (DrawerImageLoader.java:56)
  at com.mikepenz.materialdrawer.model.BaseDescribeableDrawerItem.unbindView (BaseDescribeableDrawerItem.kt:106)
  at com.mikepenz.materialdrawer.model.BaseDescribeableDrawerItem.unbindView (BaseDescribeableDrawerItem.kt:23)
  at com.mikepenz.fastadapter.listeners.OnBindViewHolderListenerImpl.unBindViewHolder (OnBindViewHolderListenerImpl.java:36)
  at com.mikepenz.fastadapter.FastAdapter.onViewRecycled (FastAdapter.kt:410)
  at android.view.View.dispatchDetachedFromWindow (View.java:21356)
  at android.view.ViewGroup.dispatchDetachedFromWindow (ViewGroup.java:3957)
  at android.view.ViewGroup.dispatchDetachedFromWindow (ViewGroup.java:3949)
  at android.view.ViewGroup.dispatchDetachedFromWindow (ViewGroup.java:3949)
  at android.view.ViewGroup.dispatchDetachedFromWindow (ViewGroup.java:3949)
  at android.view.ViewGroup.dispatchDetachedFromWindow (ViewGroup.java:3949)
  at android.view.ViewGroup.dispatchDetachedFromWindow (ViewGroup.java:3949)
  at android.view.ViewGroup.dispatchDetachedFromWindow (ViewGroup.java:3949)
  at android.view.ViewGroup.dispatchDetachedFromWindow (ViewGroup.java:3949)
  at android.view.ViewRootImpl.dispatchDetachedFromWindow (ViewRootImpl.java:5268)
  at android.view.ViewRootImpl.doDie (ViewRootImpl.java:8611)
  at android.view.ViewRootImpl.die (ViewRootImpl.java:8587)
  at android.view.WindowManagerGlobal.removeViewLocked (WindowManagerGlobal.java:522)
  at android.view.WindowManagerGlobal.removeView (WindowManagerGlobal.java:463)
  at android.view.WindowManagerImpl.removeViewImmediate (WindowManagerImpl.java:208)
  at android.app.ActivityThread.handleDestroyActivity (ActivityThread.java:5468)
  at android.app.ActivityThread.handleRelaunchActivityInner (ActivityThread.java:5744)
  at android.app.ActivityThread.handleRelaunchActivity (ActivityThread.java:5659)
  at android.app.servertransaction.ActivityRelaunchItem.execute (ActivityRelaunchItem.java:71)
  at android.app.servertransaction.ActivityTransactionItem.execute (ActivityTransactionItem.java:45)
  at android.app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor.java:135)
  at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:95)
  at android.app.ClientTransactionHandler.executeTransaction (ClientTransactionHandler.java:61)
  at android.app.ActivityThread.handleRelaunchActivityLocally (ActivityThread.java:5727)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2318)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loopOnce (Looper.java:201)
  at android.os.Looper.loop (Looper.java:288)
  at android.app.ActivityThread.main (ActivityThread.java:7932)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:548)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:942)

Here, the activity's view is being removed, which detaches it from the window. The adapter (https://github.com/mikepenz/FastAdapter/tree/develop) forwards this to the drawer library (https://github.com/mikepenz/MaterialDrawer/tree/develop) which is image-loader agnostic -- it relies on my code to load the images and cancel any in-progress loads (if e.g., the view is removed).

My code is https://github.com/pachli/pachli-android/blob/main/app/src/main/java/app/pachli/MainActivity.kt#L1301-L1322 which implements the necessary methods to load / cancel requests.

nikclayton avatar Jun 04 '25 10:06 nikclayton

I added the documentation. I'm hesitant to remove the check because even the clear flow relies on the activity not being destroyed (https://github.com/bumptech/glide/blob/b60e391a768d48b5f0b8c36fd98456662537a29a/library/src/main/java/com/bumptech/glide/manager/RequestManagerRetriever.java#L112C5-L112C16).

You can argue, we could try to change clear to not requiring the activity being not destroyed, but that may be risku.

To achieve the desired effect, can you try using RequestManager#clear, e.g.,

  glide.asDrawable().load(avatarUrl).clearOnStop().transform(...)

falhassen avatar Jun 13 '25 23:06 falhassen