Skip to content
This repository has been archived by the owner on Mar 16, 2021. It is now read-only.

Presenter not destroyed when Fragment is popped off the backstack #33

Closed
GrahamBorland opened this issue Nov 2, 2016 · 12 comments
Closed

Comments

@GrahamBorland
Copy link
Contributor

When the back key is pressed to pop a TiFragment off the backstack, the Presenter does not get destroyed. Here is the log output from TiFragment:

11-02 09:17:04.985 V: onStop()
11-02 09:17:04.985 V: onDestroyView()
11-02 09:17:04.986 V: onDestroy() recreating=true
11-02 09:17:04.986 V: onDetach()

Because the activity is not being destroyed, the destroyPresenter flag in TiFragment#onDestroy() does not get set to true. This means that the Presenter is leaked.

@GrahamBorland
Copy link
Contributor Author

GrahamBorland commented Nov 2, 2016

I don't really understand why TiFragment#onDestroy() has all that logic to decide whether or not to destroy the Presenter.

If config.shouldRetainPresenter() is true, which is the default, then the fragment has setRetainInstance(true) set in its onCreate() anyway, so the Fragment won't get destroyed on a normal configuration change. If Fragment.onDestroy() is being called by the framework, it's because the Fragment really is being properly destroyed and will not be recreated, so we should destroy the presenter too.

@passsy
Copy link
Contributor

passsy commented Nov 2, 2016

Good catch, will investigate it

@passsy
Copy link
Contributor

passsy commented Nov 3, 2016

I can verify this problem exists. But the fix isn't as easy always calling Presenter.destroy() in TiFragment#onDestroy(). The following states must be tested:

  • with/without savior
  • with/without retain
  • don't keep activities enabled/disabled
  • fragment in backstack
  • fragment attached/detached

for the following usecases:

  • call finish()
  • bring Activity in background and back to foreground
  • orientation change

Overall 72 cases, which should be tested. It was easier for the Activity where only 8 states are possible for 3 usecases (24 test). One reason I don't use Fragments anymore 😈
This also requires a delegate for TiFragment, otherwise it's not testable.
//cc #20

@passsy passsy assigned ghost Dec 12, 2016
@passsy
Copy link
Contributor

passsy commented Dec 14, 2016

Interestingly, there is a bugfix in SupportLibrary 25.1.0

  • Fragment.onDestroy() not called for fragment in backstack

Maybe this caused some of the troubles

@ghost
Copy link

ghost commented Dec 14, 2016

@passsy onDestroy() is reliably called each time a fragment gets popped off the back stack. Even in support library version 24.2.1 which is currently used by ThirtyInch.
Sadly the bugfix description does not provide any helpful informations (for example under what conditions onDestroy() was not called) :(. Nevertheless the memory leak which is described in this issue can be verified and needs to be fixed.

@passy
Copy link

passy commented Dec 14, 2016

^ s/@passy/@passsy/ :)

@StefMa
Copy link
Contributor

StefMa commented Jan 13, 2017

Because I run into the same issue I want to share some stuff.

We have a simple MasterDetail-Activity.
On phones we display the MasterFragment. On click we replace it with the DetailFragment.
On back press we pop it (because of backstack stuff).

In tablets landscape we display the MasterFragment on the left side and when we click on an item we put the DetailFragment on the right side.
On tablets portrait we have the same behaviour like phones.
Which means we have to detect the orientation change on rotation and:
Land -> Port: Remove the DetailFragment form the DetailContainer and replace it to the MasterContainer
Port -> Land: Remove the DetailFragment from the MasterContainer and replace it to the DetailContainer.

On our DetailPresenter I've put the following logic to fix the destroying:

    @Override
    public void onDestroy() {
        // Destroy presenter on phones (because they got detached). Otherwise they don't will destroy on backstackpress.
        // On tablets we put on orientation change the fragment from one container to another.
        // So we don't have to destroy the presenter but when the activity got finished
        // see https://git.io/vMzfA
        if (Utils.isPhone(getContext())) {
            MLog.d(TAG, "Destroy on Phones");
            getPresenter().destroy();
        }
        if (!mChangingConfigurations && Utils.isTablet(getContext())) {
            getPresenter().destroy();
            MLog.d(TAG, "Destroy on Tablets only when no configuration changed!");
        }
        super.onDestroy();
    }

    @Override
    public void onStop() {
        super.onStop();
        mChangingConfigurations = ((TiActivity) getActivity()).isActivityChangingConfigurations();
    }

Everything is tested with AppCompat 25.1.0

@GrahamBorland
Copy link
Contributor Author

Do we have an estimate for when a fix might be available? If it's not within the next 2-3 weeks, I'll have to find an alternative solution. :(

@passsy
Copy link
Contributor

passsy commented Mar 6, 2017

It's the last missing piece for the 0.8.0 release and will be targeted next. I can't give any time frame.

@ghost
Copy link

ghost commented Apr 19, 2017

Just to bump this issue:
@gborland As you may have noticed we are working hard on fixing (#78) this issue. Please feel free to try it out and provide your feedback.

@StefMa
Copy link
Contributor

StefMa commented Apr 25, 2017

#78 is finally merged into master.
Thank you @gborland for the report and the testing 👍

We will publish a new release soon.

@StefMa StefMa closed this as completed Apr 25, 2017
@passsy
Copy link
Contributor

passsy commented Apr 25, 2017

v0.8.0-rc4 published

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

No branches or pull requests

5 participants