Introducing: The new State Manager
Over the years, Fragments have been updated more than most Android apis. They were originally part of the Android Platform, became part of the Android Support Library, and are now part of the Jetpack component in the form of AndroidX Fragments.
Note: You should never use the Android Framework version of Fragment. Not only is it completely deprecated in Android 10, but it has not been bugged for a long time and is consistent across devices and API levels.
While Architecture Components have taken over many of the scenarios that used to require fragments (such as using LifecycleObserver to handle lifecycle callbacks, or using ViewModel to preserve state), If you use fragments to handle these scenarios, you need to interact with them by adding/removing them through the FragmentManager.
As theFragment 1.3.0 - alpha08
Release,FragmentManager
Major internal refactoring has been completed. This version replaces many with smaller, testable, maintainable (internal) classesFragmentManager
The logic in. The core class isFragmentStateManager.
Note: I’ll discuss the internals of FragmentManager in this article. If you encounter any problems with 1.3.0-ALPHA08, please submit file Issues as soon as possible to help us fix them.
Responsibilities of the new State Manager:
- Move fragments through their lifecycle methods
- Run the transition animation
- Handling delayed transactions
We did a thorough review of how these systems used to work and found that they needed to be rewritten from scratch, so we rewrote them. They’re better than ever, we were able to close at least 10 long-standing related issues, and this internal refactoring clears the way for a single FragmentManager to support multiple return stacks. Bottom Navigation manages flat interface issues) and simplifies the Fragment lifecycle.
FragmentManager moveToState()
Each FragmentManager is associated with a host, and for most fragments, Host is FragmentActivity (you can customize hosts using FragmentController and FragmentHostCallback, but it is beyond the scope of this article). When an activity moves to CREATED, STARTED, and RESUMED, the RESUMED, FragmentManager dispatts these changes to its Fragments. This is the job of moveToState(). See Jetpack for a source code perspective on the Fragment lifecycle.
Of course, it’s not that simple. There is a lot of conditional logic to determine what state a fragment should be in — the activity’s lifecycle state (or its parent fragment’s lifecycle state if it is nested) is only the first part. This is called the Max state that the fragment can be in. Maximum state ensures that activities, fragments, and their child fragments are properly nested.
So the first task of simplifying moveToState() is to pull all the logic out into one place. The FragmentStateManager was born. Each Fragment instance is bound to a FragmentStateManager. By introducing this inner class, we can extract a lot of code from the FragmentManager that interacts with the fragment (such as calling the Fragment’s onCreateView method and other lifecycle methods).
This split also enables us to write a method that uses all the backward compatible logic needed to determine what state the fragment actually should be in and centralize it in one location: computeExpectedState(). This method tracks all current states and determines what state the fragment should be in. 98% of the time, they are in the same state as host/parent Fragment, but the remaining 2% are very different from apps built on top of the fragment.
Arent we supposed to learn from the correct state: postponed fragments.
Postponed fragments
For better or worse, Fragments inherit many of the same named apis as activities. Part of this inheritance is the ability to revolve around transitions and delay transitions until the target is ready. This is important to share element transitions, while ensuring that transitions do not result in more intensive data loading.
Delayed fragments have two important features:
- Its view is already created, but not visible
- It has a lifetime upper bound of zero
STARTED
When you call startPostponedEnterTransition (after), fragments of transitions will be executed, the view will become visible, and the fragments will be moved to the RESUMED. In fact, that’s exactly what the new State Manager does. The old fragments didn’t work that way. Arent Postponed Fragments leave the Fragments and FragmentManager in an Inconsistent state.
When the fragment is delayed using postponeEnterTransition(), the expected behavior is: Fragments are added to the container will not run any queued before the into animation or the exit of the animation (the replace operation, for example) until the fragments called startPostponedEnterTransition (). Also, if the Fragment container is delayed, the fragment does not reach its RESUMED state.
However, it seems that the FragmentManager does not do this, and instead moves the Fragment and the FragmentManager as a whole into a weird, inconsistent state.
In other words, any FragmentTransaction associated with the postponed Fragment container will be “rolled back” (i.e. return to the previous Fragment), but the Fragment will not actually move to its correct state.
This leads to a number of problems;
- The Fragment view is created, but the Fragment is not added (isAdd() returns false)
- Even with commitNow(), findFragmentById() does not return the newly added fragment, but the one it replaced
- FragmentManager starts, in the state of fragments cannot be started (issuetracker.google.com/issues/1290…).
- FragmentTransaction could be executed in disorder (issuetracker.google.com/issues/1472…).
- Other animation is still running in the container (issuetracker.google.com/issues/3714…).
- OnCreateView () can be performed twice (issuetracker.google.com/issues/1439…).
In practice, solving any of these problems means having a system to replace the whole roll-back process of arent FragmentFragment, which keeps the FragmentManager consistent and up-to-date while preserving the main features of arent fragmentFragment.
Work in the Container layer
The FragmentManager has the nice attribute of Container (easy to read, but not so interesting as a maintainer), where you can pass in any Container ID you want to place the Fragment on. Even within a FragmentTransaction, you can add a fragment to one container, remove another from a different container, Replace Fragment at the top of the third container, etc.
The Fragment animation touches in and out of the Container layer.
Fragment supports a number of animation systems:
- oldpoorThe framework of
Animation
API - framework
Animator
API - framework
Transition
API (21+ only, also bad) - AndroidX
Transition
API
Naming is known to be one of the hardest problems in computer science, so when we went to build a class that could control all of these apis, it took a while to decide to use SpecialEffectsController (the class is not a public API, so the name could change). This class exists in the Container layer and coordinates all effects related to entering and exiting fragments (“special effects”).
The SpecialEffectsController is the only source for what’s really going on in the Container. This means that if the top fragment being added is delayed, the entire container will be delayed. The FragmentManager layer logic is no longer required, nor is any rollback of the transaction required (which, as we mentioned earlier, affects multiple Containers). Therefore, if the FragmentManager is in the correct state, we can still get all the properties of the deferred fragment.
This API also allows us to consolidate all the crazy effects apis of fragments into oneDefaultSpecialEffectsController
The Controller is responsible for running transition, Animation, and animator. Again, the logic scattered in the FragmentManager moves to one place.
So ‘New State manager’ is something
It means replacing this architecture:
Old State Manager: All logic is in FragmentManager
It looks more like this:
New State Manager: The FragmentManager communicates with individual FragmentStateManager instances that coordinate with other fragments in the Container through SpecialEffectsController.
By splitting the internal structure of the FragmentManager, the logic for each layer is greatly simplified:
FragmentManager
Only has a state that applies to all fragmentsFragmentStateManager
Manage state at the Fragment layerSpecialEffectsController
Manage state at the Container layer
This separation of responsibilities has allowed us to expand our test suite by nearly 30%, covering more scenarios that are almost impossible to test alone.
Should I see a change in behavior?
Not at all. In fact, we do a large portion of fragment testing for both old and new state managers, specifically to ensure that we have a strong set of regression tests.
However, if you rely on inconsistent state, you can put the FragmentManager into a delayed fragment, and, yes, you’ll find that you’ve actually got the correct state. In the release notes, you’ll find a list of bug fixes related to the new state manager, so read carefully to make sure your problem is not caused by your own fix, which is an old behavior that you can remove now.
Similar to the onDestroyView timing change in Fragment 1.2.0, the new state manager will keep the Fragment in the STARTED state, Until its transitions/animations/animators/special effects all finished, keeping all the fragments are consistent, Arent they postponed directly or because of other fragments in the same container?
What if I do see a behavioral change?
The new status manager is enabled by default. If you see different behavior in your app than before, use the new experimental API first to check if it’s due to the new state manager.
The new state manager is enabled by default when you update Fragment 1.3.0-alpha08. If you see different behavior in your app than before, use the new experimental API first to check if it’s due to the new state manager.
State manager for China Launch. If you notice a difference between your application and the past, you can first use the new experimental API to check if it is relevant to the new state manager:
FragmentManager.enableNewStateManager(false)
Copy the code
This API lets you revisit the old world and verify that any changes you see that are inconsistent with the previous ones are due to the new state manager. If it is confirmed that the new state manager is to blame, you can build a sample project to reproduce the problem you encountered and make an Issue here.
Note: FragmentManager enableNewStateManager () API is experimental.
This means that it is not considered part of the Fragment stabilization API and can be removed at any time. Removing all the old code can save a lot of code, but given the importance of handling code properly, we may have to wait until a stable version of Fragment 1.3.0 is released before removing the API, such as when Fragment 1.3.1 is released.
Over 100 personal changes were made in 11 months, definitely the largest internal change to Fragment in a while, and it allowed us to build more maintainable, sustainable and understandable code. This means more consistent behavior across fragments, and it becomes a solid foundation on which you can build your applications. We appreciate your continued questions and feedback.
Thanks to Jeremy Woods, Chet Haase, and Nick Butcher.
Finish the translation.
The translator to summarize
In his talk on Fragments: Past, Present, and Future (Android Dev Summit ’19), Ian Lake discussed the Future of Fragments. The official nuggets article is here.
- Fragment communication problem
- Many return stack
- Simplify the Fragment life cycle
Deprecating the targetFragment API and using the new FragmentResult API has been released: Update Jetpack 1.3.0- Alpha04, new posture for Fragment communication
The Fragment life cycle is being simplified, for example onActivityCreated is deprecated, and the details of onActivityCreated are now deprecated in Jetpack update fragments. It’s still a long way from merging the life cycle of the Fragment itself with the View inside it.
The multiple return stack has been blocked by the issue addressed in this article, which covers the past 11 months from Last September to Android Dev Summit ’19. The following issue was created in May 2018.
Fortunately, the stumbling block of multiple return stacks has been solved, but here we can also see the optimization cost after sdK-level code is difficult to maintain. In this paper, the fragment state management logic is rewritten, and the old logic is still retained and the entry for switching is left even after a lot of testing.
Looking at changes in Fragment code, it’s clear how important “single function” is to building maintainable, understandable, testable, and robust code.
For more information on fragments, see
-
Changes to using activities and fragments under AdroidX
-
Can you really use a Fragment? Fragments FAQ and new postures for using fragments on androidx
-
AndroidX Fragment1.2.2 Source code analysis
-
Fragment returns the stack preparation section
-
Jetpack’s Fragment return stack is a Fragment return stack demo