Skip to content
This repository has been archived by the owner on Apr 19, 2022. It is now read-only.

disposing render subscription after onDestroyView #44

Open
kioba opened this issue Aug 12, 2018 · 15 comments
Open

disposing render subscription after onDestroyView #44

kioba opened this issue Aug 12, 2018 · 15 comments

Comments

@kioba
Copy link

kioba commented Aug 12, 2018

Issue:

this might be an issue if an async event comes back and triggers a render between the onDestroyView and onDestroybecause the fragment view is already destroyed with the onDestroyView but we did not unsubscribe the render subscription.

Clearing the disposables in the fragments happening with the onDestroy event:

override fun onDestroy() { super.onDestroy() disposables.dispose() }

fragment lifecycle:
... onStop onDestroyView onDestroy

Reproducing the issue:
this could be reproduced if you call detach after binding the ViewModel and delaying the state observable.
detach: detach triggers the destruction of the view without calling onDestroy
delay: simulates the late render event

Solution suggestion:
Moving up the bind event to the onStart() and the unsubscription to the onStop() ?

@oldergod
Copy link
Owner

How about just moving disposables.dispose() before the super.onDestroy() ?

@kioba
Copy link
Author

kioba commented Aug 15, 2018

onDestroyView is a different lifecycle event for fragments, Moving the disposables.dispose() before the super.onDestroy() does not solve it.

calling detach on a fragment inside a fragmentTransaction actually triggers only onDestroyView without triggering onDestroy

Do you think the start() and stop() could be an issue?

@oldergod
Copy link
Owner

The only thing I can think of is that if we move those to start() and stop(), we probably would have to change how we trigger the initial intent.
Basically, the app is open, you switch to another app and come back: on start(), I don't want the view to reload stuff it should already have, I'd like it to reuse the latest view model available.

@kioba
Copy link
Author

kioba commented Aug 23, 2018

If the initial intent filter is used we take only the first initial intent and the rest won't be triggered as long as the ViewModel is not destroyed.

@oldergod
Copy link
Owner

Oh right, I forgot about that one. Would you like to submit a PR?

@kioba
Copy link
Author

kioba commented Aug 27, 2018

yes, happy to do it 👍

@nateridderman
Copy link

I came here wondering about the same issue. I think the proposed solution makes sense.

@nateridderman
Copy link

nateridderman commented Sep 6, 2018

I was having trouble with binding in onStart and unbinding in onStop. When I leave the app and come back, it's not subscribed to the render calls. I'm not sure why, but switching to onActivityCreated/ onDestroyView worked for me, which is the scope of my ViewModel. The only reason I used onActivityCreated instead of onCreateView was because I needed the activity available for unrelated reasons.

@nateridderman
Copy link

Figured out my issue with onStart/onStop. I was calling disposables.dispose() instead of disposables.clear() in onStop, preventing it from accepting new disposables. @kioba lmk if you get stuck on a PR

@kioba
Copy link
Author

kioba commented Sep 11, 2018

@nateridderman the disposables.clear() solved an issue where returning to the list of TODOs the last added one was never shown. Thanks for the suggestions! 👍

@oldergod
Copy link
Owner

Thank you all.

@oldergod
Copy link
Owner

I had an after thought and I see the fix wasn't the right one, it also created #49 . Not sure how to deal with fragment lifecycle still; I'd rather move away from it maybe.

@oldergod oldergod reopened this Feb 12, 2019
@kioba
Copy link
Author

kioba commented Feb 13, 2019

I will go and figure out why the refresh event is triggered.
What I can see is if we change the initial intent solution it won't trigger multiple times on the onStart:

  private fun initialIntent(): Observable<TasksIntent> {
    return Observable.fromCallable { TasksIntent.RefreshIntent(false, "INITIAL") }
        .cast(TasksIntent::class.java)
        .startWith(TasksIntent.InitialIntent)
  }

OnStart should not be called more than once before onStop and could not reproduce it. I am not

Not sure how to deal with fragment lifecycle still; I'd rather move away from it maybe.

Could you please give an example of what else could be improved? I am also interested in what is your idea behind the move away from fragments.

Happy to help solve these issues

EDIT: I could not reproduce the same issue with pullToRefresh.

@oldergod
Copy link
Owner

This very issue because caused by the weirdness of fragments' lifecycle is a good enough reason for me to not use them.
Since there's a 1:1 relationship to activities and fragments in this project, I think deleting fragments and only use activities is a good alternative.
Or maybe just let the activity, and not the fragment, manage the stream lifecycle.

@7-cat
Copy link

7-cat commented Jul 28, 2019

override fun render(state: xxxViewState) {
if (view == null) {
return
}
...
}
I'm not sure this help?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants