Fragments are a part of most Android apps. It’s an API made available by Google since 3.0 (Honeycomb or API level 11) to help make reusable UI components between Phone and Tablet configurations. In this article I will explain why we shouldn’t use them anymore and show you some alternatives.
Why do we use fragments?
I asked myself and others about why we use fragments. Almost everyone replied: “Because it’s in the Android framework and everyone else is doing it”. I wouldn’t settle for a response like this, so I started searching for objective reasons, asking questions like: What problem does it solve? How does this help my code be cleaner? Why should I use it?
Here’s the list of legitimate reasons to use fragments:
- Composition of complex UI
- Lifecycle methods
- Toolbar customizations
- Backstack navigation
- Transactional UI Updates
- State preservation
The list is compelling, however there’s also a list of reasons why we should start searching for alternatives.
Reasons not to use fragments:
Like all things in life, fragments have a Light side and a Dark side:
- Making a transaction to update the UI is an interesting concept, but other than the fact the FragmentManager API is built around it, I can count on my closed hand (zero) exactly how many times I used it to actually solve a real world problem.
- The backstack for navigation is a useful thing, although it’s needlessly complicated. A colleague of mine discovered an edge case when the stack is broken. The steps are a bit complex involving screen rotation, but ended up with pushing a fragment to the backstack that ended up in the middle of the stack instead of the top. I’ll put a link here once he open sources the example.
- Support library updates sometimes cause the fragments to behave in new and unexpected ways, especially if you have very complex UIs composed with nested fragments. It can get out of hand quite easily. This could be balanced by the fact that updates also solve bugs.
- Fragment state management is incomprehensible. I challenge any daring Java enthusiast to take a close look under the hood at FragmentManagerImpl. In around 2000 lines of code and a complexity beyond any normal human can comprehend, I’m shocked and also in awe of the guys maintaining that codebase, but I also fear that bugs are just waiting to happen.
I dedicated a special section in this article to FragmentPagerAdapter, because it deserves the attention.
You can find the complete source code here: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.2_r1/android/support/v4/app/FragmentPagerAdapter.java
Below are a few parts I find interesting:
instantiateItem() starts a transaction, gets the fragment tag (name?), if the fragment is found in the manager it’s attached, if it’s not, one is obtained using the getItem() method and added to the transaction, and it also does something with the menu. That’s a lot of responsibility for a method.
mCurTransaction is private so if you want to change the implementation of instantiateItem() you’re out of luck because there’s no way of doing that without breaking the transaction.
I would expect getItem() to be a getter, instead it has a parameter “position” and is responsible for creating the fragment. The javadoc for this method doesn’t help in explaining this behavior either.
makeFragmentName(int viewId, int index) is probably the most annoying thing about this class, especially when you’re trying to get a reference to the fragment at a certain position. This method is private and static, which means this mechanism can’t be extended. It forces you to hardcode this string.
Each point in favor of using Fragments in your Android application has an alternative using custom views.
Views in Android are structured in hierarchies, you can leverage that. Composition of complex UI is not, in fact, a special Fragment only feature. The way you insert custom behaviour and rendering logic inside Fragments, you can do the same by extending any ViewGroup.
Lifecyle methods are a tricky subject, even for experienced Android developers. When using custom views instead of fragments, you can simply delegate the lifecycle events from the activity to the custom view.
Customizing the toolbar can be achieved in the same way any other lifecycle method is delegated:
The backstack of the FragmentManager shouldn’t be a hard component to simulate. If we have all the information to recreate a fragment and Stack implementation from Java, we can have a better control over the backstack for navigation.
Here’s a simple implementation of a backstack:
You just need to keep a NavItem stack somewhere. A NavItem contains the class of the custom view and the arguments (the information needed to recreate the view). You push to the navigation stack whenever you add/replace the custom view. OnBackPressed you pop the stack and recreate the previous view using the information in the NavItem.
If you have some complicated way to create the views, you can always replace the code here to use a view factory or any other paradigm. This is what you would expect the FragmentManager to do. If adding these changes to the activity is not feasible for your situation, the logic can instead be externalized and reused during configuration changes. FragmentManagerImpl has about 2000+ lines of code to handle this, plus some in other classes because it has to manage really complex state transitions. The solution above is a much simpler implementation, less prone to errors or inconsistent states.
State preservation can be achieved easily using methods available in the View class View.onSaveInstanceState() and View.onRestoreInstanceState(). Alternately, you can integrate IcePick, an open source library that helps to maintain state across configuration changes: https://github.com/frankiesardo/icepick
“Anger, fear, aggression… the dark side are they, once you start down the dark path, forever will it dominate your destiny.” — (Yoda)
As developers we need software components that make our lives easier. We should think critically about how frameworks and libraries that we are using are designed. Just because Google promotes a certain framework or method it doesn’t mean it’s always the best option. In case of Fragments, it’s clear that the design needs some work. In the meantime, custom views allow for a cleaner and less error prone code.