New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

You cannot start a load for a destroyed activity error #1097

Closed
rsaphala opened this Issue Mar 27, 2016 · 13 comments

Comments

Projects
None yet
4 participants
@rsaphala
Copy link

rsaphala commented Mar 27, 2016

Glide Version: 3.7.0

Integration libraries: OkHttp3

Device/Android Version: All devices

Issue details / Repro steps / Use case background:
I have a fragment that contains a RecyclerView that loads Images using Glide. When the user scrolls really fast to the bottom and quits the app, the error occurs.

Glide load line / GlideModule (if any) / list Adapter code (if any):

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (holder instanceof PostsViewHolder){
        HomeFeedPosts.data postsData = postsList.get(position);

        ((PostsViewHolder) holder).title.setText(postsData.user.username);

        Glide.with(((PostsViewHolder) holder).imagePost.getContext())
                .load(postsData.images.standard_resolution.url)
                .priority(Priority.IMMEDIATE)
                .placeholder(R.drawable.grey_placeholder)
                .into(((PostsViewHolder) holder).imagePost);

        Glide.with(((PostsViewHolder) holder).imageAvatar.getContext())
                .load(postsData.user.profile_picture)
                .priority(Priority.LOW)
                .into(((PostsViewHolder) holder).imageAvatar);


    } else if (holder instanceof ProgressBarViewHolder){
        ((ProgressBarViewHolder) holder).progressBar.setIndeterminate(true);
    }
}

Stack trace / LogCat:

java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity
at com.bumptech.glide.manager.RequestManagerRetriever.assertNotDestroyed(RequestManagerRetriever.java:134)
at com.bumptech.glide.manager.RequestManagerRetriever.get(RequestManagerRetriever.java:102)
at com.bumptech.glide.manager.RequestManagerRetriever.get(RequestManagerRetriever.java:87)
at com.bumptech.glide.Glide.with(Glide.java:629)
at com.saphala.gokilpedia_mobile.adapters.HomeFeedAdapter.onBindViewHolder(HomeFeedAdapter.java:86)
at android.support.v7.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:5465)
at android.support.v7.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:5498)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4735)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4611)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1988)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1384)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1347)
at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1174)
at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:1031)
at android.support.v7.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:4055)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
at android.view.Choreographer.doCallbacks(Choreographer.java:574)
at android.view.Choreographer.doFrame(Choreographer.java:543)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5113)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:609)                                                                                    
at dalvik.system.NativeStart.main(Native Method)
@TWiStErRob

This comment has been minimized.

Copy link
Collaborator

TWiStErRob commented Mar 27, 2016

Hmm, that's an interesting repro for this recurring issue. There's likely not a good way around it inside Glide, I suggest one of these workarounds (in descending order of quality):

  1. Pass in a Glide object: new HomeFeedAdapter(Glide.with(this))

    class HomeFeedAdapter extends Adapter {
        private final RequestManager glide;
        HomeFeedAdapter(RequestManager glide) {
            this.glide = glide;
        }
        @Override public void onBindViewHolder(ViewHolder holder, int position) {
            glide.load....
        }
    }

    This is a handy workaround, and it also makes your adapter flexible to use with fragment and activity as well; and the code cleaner: view.getContext() is usually a long way to access Glide.
    See http://stackoverflow.com/a/32887693/253468 why it's even an improvement.

  2. list.setAdapter(null) in onDestroy() or onStop(), which is incovenient to do all the time

  3. Check for ((Activity)view.getContext()).isDestroyed() or similar method, which may be unsafe and is even more inconvenient to do all the time

  4. Use Glide.with(context.getApplicationContext()), see http://stackoverflow.com/a/32887693/253468 why it may not be a good idea.

@TWiStErRob TWiStErRob added the question label Mar 27, 2016

@TWiStErRob

This comment has been minimized.

Copy link
Collaborator

TWiStErRob commented Mar 27, 2016

Curious: How do you "quit the app" quick enough that the items are binding too late?

@rsaphala

This comment has been minimized.

Copy link
Author

rsaphala commented Mar 27, 2016

There's an OnScrollListener attached to the RecyclerView that loads more data from the server as you scroll to the bottom. To reproduce this issue simply scroll really fast and press the back button.

@TWiStErRob

This comment has been minimized.

Copy link
Collaborator

TWiStErRob commented Mar 27, 2016

Ah, I see, so your lazy loading delivers data to a dead activity, it's the same issue as the others: there was always a rogue async call involved.

@rsaphala

This comment has been minimized.

Copy link
Author

rsaphala commented Mar 27, 2016

I might be wrong on this, but doesn't the first option create a memory leak?

@TWiStErRob

This comment has been minimized.

Copy link
Collaborator

TWiStErRob commented Mar 27, 2016

I think at worst there's a reference cycle, which doesn't prevent GC from doing its job.

  • Activity references RecyclerView
  • Views reference Activity (getContext)
  • RecyclerView references Adapter
  • Adapter references RequestManager
  • RequestManager references Activity (not sure)
  • Activity references RequestManager through hidden RequestManagerFragment

RequestManager is aware of the activity/fragment lifecycle so any request will be cleared when they die, see linked SO. I don't think anything will be kept after destroy as the fragment is also destroyed.

@rsaphala

This comment has been minimized.

Copy link
Author

rsaphala commented Mar 27, 2016

Ahh ok thanks for clearing that up! The issue is now fixed.

@kuldiep

This comment has been minimized.

Copy link

kuldiep commented Jul 20, 2016

hii...m stuck with java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity
at com.bumptech.glide.manager.RequestManagerRetriever.assertNotDestroyed

i have three activities in my app..so when i am starting 2nd activity there are some images which are loaded by glide in AsyncTask then i m going to further activity...again i came back to 2nd activity("no error") then i come back to 1st activity now again when i am going to 2nd activity app crashes with that exception...i tried glide.clearMemory()..but it dosent work :( please help me out

@TWiStErRob

This comment has been minimized.

Copy link
Collaborator

TWiStErRob commented Jul 20, 2016

@kuldiep I can only say the usual: make sure you don't start a load when the activity is destroyed or being destroyed. You can cancel the async task when the activity ia finishing (e.g. onStop). Please open a new issue with some code and more details, if you want more help.

@kuldiep

This comment has been minimized.

Copy link

kuldiep commented Jul 20, 2016

thanks for help....i think i come up with another solution and it works..i m just clearing glide.get(this).clearMemory in onResume, onRestart nd onDestroy methods..as of now i am not getting any error :)

@TWiStErRob

This comment has been minimized.

Copy link
Collaborator

TWiStErRob commented Jul 20, 2016

@kuldiep You might as well just disable memory cache altogether (similar applies to bitmap pools): use MemoryCacheAdapter as seen in wiki; and remember, this is a not a solution you found, but a hack hiding the real problem.

@kuldiep

This comment has been minimized.

Copy link

kuldiep commented Jul 20, 2016

ya my approach is not a solution but i'll keep your approach of stopping async task in onStop method for letter use..thanks

@tangyiwu

This comment has been minimized.

Copy link

tangyiwu commented Jan 30, 2018

I think the way is that we pause request before activity destroyed.

@Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_BACK) {
+            Glide.with(this).pauseRequests();
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public void finish() {
+        Glide.with(this).pauseRequests();
+        super.finish();
+    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment