Zhuinden / simple-stack

[ACTIVE] Simple Stack, a backstack library / navigation framework for simpler navigation and state management (for fragments, views, or whatevers).
Apache License 2.0
1.36k stars 76 forks source link

Ability to reuse created fragments in history #235

Closed topnax closed 4 years ago

topnax commented 4 years ago

Let's have a following fragment transactions: A => B => C (currently active). Now imagine going back in the history, say we call Navigator.backPress(context) one time. The B fragment is at the top of the stack and should be recycled, it's onCreateView method should not be called. The current behavior is such that fragment B is now detached from the container activity and it's view is recreated and not reused, thus consuming unnecessary CPU time.

Would it be possible to recycle fragments in the history that were previously shown?

Could you provide any hints on how to change the DefaultFragmentStateChanger or implement it yourself? I think that it is one of the key features that every fragment state changer should implement in my opinion.

topnax commented 4 years ago

In quick thoughts: calling setHistory() should detach all fragments. Calling goTo and replace should behave as I have written.

Zhuinden commented 4 years ago

Hey,

This was actually thoroughly discussed in https://github.com/Zhuinden/simple-stack/issues/133#issuecomment-495859930 .

The current behavior is such that fragment B is now detached from the container activity and its view is recreated and not reused, thus consuming unnecessary CPU time.

I fully agree, however using show/hide does not trigger onStop/onStart (thus could create unpredictable behavior when using LiveData), while using setMaxLifecycle(CREATED) destroys the view just like attach/detach.

As such, I cannot make it the default behavior, even though I theoretically agree with you.

Nonetheless, it is possible to customize the behavior to use show/hide instead of attach/detach through the following means:

        fragmentStateChanger = object: DefaultFragmentStateChanger(supportFragmentManager, R.id.root) {
            override fun isNotShowing(fragment: Fragment): Boolean {
                return fragment.isHidden;
            }

            override fun startShowing(fragmentTransaction: FragmentTransaction, fragment: Fragment) {
                fragmentTransaction.show(fragment);
            }

            override fun stopShowing(fragmentTransaction: FragmentTransaction, fragment: Fragment) {
                fragmentTransaction.hide(fragment);
            }
        }

But the original limitation is not actually on my side, it's on the Fragment side. If you were using replace().addToBackStack() (as Jetpack Navigation does), they also destroy the view as you navigate forward then back - and Fragments were designed to suit their requirements.

Zhuinden commented 4 years ago

With that, the currently available only alternative that retains the views (show/hide) is demonstrated, and this question is technically resolved.

Just beware that it doesn't alter the Fragment's lifecycle (and trigger onStop). And Fragments are currently unable to trigger onStop without also destroying their views. It's an unfortunate limitation of Fragments.