Fragments in Android are UI controllers that take ownership of a part of the application’s screen. Unlike Activities, which consume the entire app’s screen, you can combine and nest multiple Fragments on a single screen to build modular, reusable and performant user interfaces in Android applications.
In this post, I’ll review the history of Fragments and then discuss Fragment Lifecycle, which is one of the trickiest concepts in Android development.
History of Fragments in Android
Fragments were introduced into Android framework in 2010 by Dianne Hackborn. That’s her original commit message:
Author: Dianne Hackborn <hackbod@google.com>
Date: Thu Apr 15 14:45:25 2010 -0700
Introducing Fragment.
Basic implementation of an API for organizing a single activity into separate, discrete pieces. Currently supports adding and removing fragments, and performing basic lifecycle callbacks on them.
The idea of “organizing a single activity into separate, discrete pieces” was absolutely great. However, unfortunately, Fragments have had a troubled history and earned a controversial reputation among Android developers.
In no small part, Fragments’ controversial reputation was the result of the complexity of Fragment Lifecycle. Just take a look at this diagram – isn’t it intimidating!? In addition, historically, there were many bugs and inconsistencies with this component, so early Fragments adopters struggled a lot.
Luckily, many of the earlier pain points have been resolved by now. In the recent years, Google even decided to push the ecosystem in the direction of “single-Activity applications”, so Fragments gained focus and love. Many old bugs were fixed, some less relevant features deprecated and new useful features added.
All in all, today, Fragments are very popular approach for implementing “screens” in Android applications and, in many cases, even projects that adopt Jetpack Compose keep using them.
Activity Lifecycle
As you’ll see shortly, when I use Fragments, I try to decouple them from Activity lifecycle as much as possible. Basically, Fragments shouldn’t know anything about the Activity they’re hosted in.
However, Fragment lifecycle have many similarities to Activity lifecycle. In some sense, Fragments are light-weight Activities. I already wrote an article about Activity’s lifecycle, and, in this post, I’ll draw many analogies between them. However, I don’t want to repeat myself. Therefore, I’m going to assume that you already read that article about Activities. If you haven’t, please do it now before you proceed with this post.
My Take on Fragment Lifecycle
My approach to Fragment’s lifecycle aims to achieve two goals:
- Make Fragment’s lifecycle handling logic as similar as possible to Activity’s logic.
- Make Fragment’s lifecycle handling logic independent of Activity lifecycle.
Let’s discuss each of these goals in more details.
By handling Fragment’s lifecycle similar to Activity’s lifecycle I greatly reduce the overall complexity of the application. Developers will need to learn just one approach instead of two different ones. This means less effort during development, easier maintenance and quicker ramp-up of new team members. I’m also completely sure that doing so reduces risk of bugs, though that’s subjective.
Making Fragment independent of Activity’s lifecycle goes back to the diagram of their respective lifecycles. Each of them is extremely complex, but still manageable in isolation. However, thinking about both lifecycles and the inter-dependencies between them is beyond what I’m capable of.
By satisfying this two goals I greatly reduce the complexity associated with Fragments thus making them more attractive.
onCreate(Bundle)
Remember that you don’t have access to Activity’s constructor so you can’t use it to inject dependencies into Activity? Well, the good news is that Fragment does have a public constructor, and we can even define additional ones. The bad news is that doing so will lead to serious bugs, so we can’t really do it.
Android will destroy and then re-create Fragments during so called save & restore flow. The re-creation mechanism uses reflective calls to Fragment’s no-arguments constructor. So, if you used constructor with arguments to instantiate the Fragments in the first place and passed dependencies into it, all of these dependencies will be set to null after save & restore. Not cool.
Therefore, just like with Activities, you’ll use onCreate(Bundle)
method as a replacement for constructors. Dependency injection and initialization of Fragment’s members takes place here.
However, there is one major difference form Activity’s onCreate(Bundle)
: you must not touch or do anything related to Android Views in Fragment’s onCreate(Bundle)
. This is very important. The reason for that will become clear in the next section.
All in all, Fragment’s onCreate(Bundle)
will look similar to this:
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); getInjector().inject(this); // inject dependencies if (savedInstanceState != null) { mWelcomeDialogHasAlreadyBeenShown = savedInstanceState.getBoolean(SAVED_STATE_WELCOME_DIALOG_SHOWN); } }
Update: since I originally wrote this article, it became possible to instantiate Fragments using non-default constructors with arguments. However, to do that, you’ll need to use a new component called FragmentFactory. In my opinion, this additional complexity is justified only if you want to use dependency injection in multi-module projects. For more details, read this post about FragmentFactory.
onCreateView(LayoutInflater, ViewGroup, Bundle)
This method is unique to Fragment and it’s the most prominent difference between Activity and Fragment lifecycle.
I will get a bit ahead of myself and tell you that this method is also the “root of all evil” associated with Fragments. I will discuss this “evil” later, but just keep in mind that if you’re using Fragments, you better not underestimate the complexity of onCreateView(LayoutInflater, ViewGroup, Bundle)
.
So, what’s the story?
Activities operate under assumption of being associated with a single View hierarchy for the entire lifecycle. You initialize this hierarchy in Activity’s onCreate(Bundle)
and it stays attached to it until garbage collected. You can manually change the composition of Activity’s View hierarchy, but Android will not do anything like that on your behalf.
Fragments, on the other hand, operate under assumption that Android should be capable of destroying and re-creating the View hierarchy associated with a specific Fragment instance. In other words, Fragments can have multiple View hierarchies during their lifecycle and it’s up to Android to decide when the replacement takes place.
Given that View hierarchy can be replaced at runtime, it should be clear now why you mustn’t touch it in Fragment’s onCreate(Bundle)
. This method will be called just once after the fragment is attached to the enclosing Activity, therefore it can’t support the dynamic nature of Fragment’s View hierarchy.
Enter onCreateView(LayoutInflater, ViewGroup, Bundle)
.
This method will be called by Android every time a new View hierarchy needs to be created. Your job is to create and initialize View hierarchy to the correct state, and then return it from this method. Android will take care of it from there.
The main rule for implementations of this method is: all Fragment’s members holding references to objects related to View hierarchy must be assigned inside onCreateView(LayoutInflater, ViewGroup, Bundle)
. In other words, if Fragment holds references to Views or related objects in its fields, make sure that all these fields are assigned in this method. This is really important.
All in all, the general form of onCreateView(LayoutInflater, ViewGroup, Bundle)
should be something along these lines:
@Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View root = inflater.inflate(R.layout.some_layout, container, false); mSomeView = rootView.findViewById(R.id.some_view); // must assign all Fragment's fields which are Views mLstSomeList = rootView.findViewById(R.id.lst_some_list); // must assign all Fragment's fields which are Views mAdapter = new SomeAdapter(getContext()); // must assign all Fragment's fields related to View hierarchy mLstSomeList.setAdapter(mAdapter); return rootView; }
Another interesting thing about this method is that it also receives Bundle with saved state. Honestly, I find it troubling. It’s like the developers weren’t sure where exactly the state should be restored, so they injected this Bundle into several methods for us to figure it out ourselves.
Don’t restore state in this method. I will explain why when we talk about onSaveInstanceState(Bundle)
.
Edit:
User Boza_s6 provided his (her?) feedback for this article on Reddit, and we had a very interesting discussion. The question was whether my method can introduce memory leaks if lists and adapters are used in Fragments. In light of this discussion, I would like to make sure this subject is clear.
There is no risk of memory leaks if you follow the rules I share in this article. In fact, part of the reasons I use this approach is to mitigate Fragment’s intrinsically higher risk of memory leaks.
The rule for onCreateView(LayoutInflater, ViewGroup, Bundle)
is that every Fragment’s field related to View hierarchy must be assigned in this method. This includes list Adapters, user interaction listeners, etc. The only way to keep Fragment’s code maintainable and free of tricky corner cases is to ensure that this method re-creates the entire View hierarchy and all the associated objects from scratch.
onStart()
This method in Fragment has exactly the same responsibilities and guidelines as Activity’s onStart()
. You can read about it my previous article about Activity’s lifecycle.
So, the general form of Fragment’s onStart()
will be something like this:
@Override public void onStart() { super.onStart(); mSomeView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { handleOnSomeViewClick(); } }); mFirstDependency.registerListener(this); switch (mSecondDependency.getState()) { case SecondDependency.State.STATE_1: updateUiAccordingToState1(); break; case SecondDependency.State.STATE_2: updateUiAccordingToState2(); break; case SecondDependency.State.STATE_3: updateUiAccordingToState3(); break; } if (mWelcomeDialogHasAlreadyBeenShown) { mFirstDependency.intiateAsyncFunctionalFlowAndNotify(); } else { showWelcomeDialog(); mWelcomeDialogHasAlreadyBeenShown = true; } }
As you see, this is the place to put most of Fragment’s functional logic. The fact that this approach is consistent between Activity and Fragment is of huge benefit.
Update: just like with Activities, you no longer need to register View listeners in onStart()
and unregister in onStop()
because the behavior that required that was fixed at the framework level and backported to API 19. Just like with Activities, I decided to keep this discussion for historical reasons and developers who still support APIs lower than 19.
onResume()
Handling of this method is identical to Activity’s onResume()
.
onPause()
It’s identical to Activity’s onPause()
as well.
onStop()
Again, this method is identical to Activity’s onStop()
. It should follow the following pattern:
@Override public void onStop() { super.onStop(); mSomeView.setOnClickListener(null); mFirstDependency.unregisterListener(this); }
The interesting and surprising part here is unregistration of the click listener. I explained why you might want to do that in the post about Activity lifecycle, so I won’t repeat myself. However, I would like to use this opportunity to address the question I’ve been asked in context of the previous post: is this unregistration mandatory?
Well, to the best of my knowledge, absolute majority of Android applications don’t do it and are still doing fine. So, I think it’s not mandatory. That said, if you don’t do it you can expect to see a certain amount of errors and crashes due to spurious notifications if you get to scale. So, not mandatory, but it’s not like it’s a premature optimization. We do know that this is rare, but real issue.
I try, as much as possible, to use my own MVC architectural pattern for organization of applications’ presentation layer. In this approach I can “disconnect” from UI by writing just a single line of code. Therefore, if I get to use this pattern, I always disconnect the UI in onStop()
.
Update: as I wrote in the discussion of onStart()
, the issue I’m talking about was fixed, so you don’t need to unregister View listeners in onStop()
.
onDestroyView()
You should not override this method in absolute majority of the cases. I guess some of the readers will be surprised to read this, but I really mean it.
As I said, you must assign all Fragment’s fields which are Views in onCreateView(LayoutInflater, ViewGroup, Bundle)
. This requirement stems from the fact that Fragment’s View hierarchy can be re-created, so any View reference you don’t initialize in that method will be either null, or point to an object from the old (now discarded) hierarchy. Doing this is very important because otherwise you might get some really nasty bugs, crashes and memory leaks.
If you follow the above recommendation for onCreateView(LayoutInflater, ViewGroup, Bundle)
, Fragment will hold strong references to its Views until onCreateView(LayoutInflater, ViewGroup, Bundle)
will be called the next time, or the entire Fragment is destroyed. However, there is no risk of memory leak or any other potential issue in that.
Now, there is a widespread complementary recommendation that you should set all the aforementioned references to View fields to nulls in onDestroyView()
. The motivation is to release these references as quickly as possible to allow garbage collector to claim them right after onDestroyView()
returns. This will free the memory associated with these Views much earlier. In my opinion, that’s classic case of premature optimization. You don’t really need this optimization in absolute majority of the cases. Therefore, there is no need to complicate the already complex Fragment lifecycle handling with override of onDestroyView()
.
So, you don’t need onDestroyView()
.
onDestroy()
Just like with Activities, there is no need for you to override this method in Fragments.
onSaveInstanceState(Bundle)
The implementation of this method is essentially the same as of its Activity’s counterpart:
@Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(SAVED_STATE_WELCOME_DIALOG_SHOWN, mWelcomeDialogHasAlreadyBeenShown); }
I remind you not to be mislead by the seeming simplicity of this method. Incorrect handling of save & restore flow is one of the major causes for bugs and crashes in Android applications.
Javadoc of this method was written by Dianne Hackborn back in February 2011 and contains this really strange part:
This corresponds to Activity.onSaveInstanceState(Bundle) and most of the discussion there applies here as well. Note however: this method may be called at any time before onDestroy(). There are many situations where a fragment may be mostly torn down (such as when placed on the back stack with no UI showing), but its state will not be saved until its owning activity actually needs to save its state.
According to official documentation “this method may be called at any time before onDestroy()”. There are two major issues with this.
First of all, as Dianne explains, the View hierarchy associated with Fragment can be destroyed without actually saving the state. Therefore, if you’d restore Fragment’s state in onCreateView(LayoutInflater, ViewGroup, Bundle)
, then you would risk overriding the latest state with a stale one. State corruption is a very serious bug. That’s why I told you to restore the state in onCreate(Bundle)
only.
The second issue is that if onSaveInstanceState(Bundle)
can be called at any time before onDestroy()
, then you don’t have guarantees as to when it’s safe to change Fragment’s state (e.g. replace nested Fragments).
Now, I don’t think that this description is accurate. In fact, I think that it had already been wrong when Dianne Hackborn wrote it back in 2011. The notion of “any time” implies some kind of nondeterminism or randomness which I think has never been there.
The good news is that this documentation might be inaccurate (surprisingly, inaccurate documentation can be “good news”). From review of some of AOSP and Support Libraries source it looks like the guarantees for onSaveInstanceState(Bundle)
in Fragments are basically the same as in Activities. To be on the safe side, I opened this bug in Android issue tracker and you’re encouraged to star it such that we get the required clarifications faster.
setRetainInstance(boolean)
Never use retained Fragments. You don’t need them.
If you do, keep in mind that it changes Fragment’s lifecycle. So nothing you read in this article will be guaranteed to work anymore.
Update: retained Fragments became deprecated since I originally wrote this article.
Why Fragments are So Complicated
As you can see, Fragments are indeed very complicated beasts. I would even say too complicated.
The biggest issue with Fragments is that their View hierarchy can be destroyed and re-created independently of the Fragment object itself. If that wouldn’t be the case, Fragment’s lifecycle would be almost identical to Activity’s one. What was the reason for this complication? Well, obviously I don’t know the answer and can only speculate based on my limited understanding. Therefore, take the following discussion with a grain of salt.
I think that this mechanism was introduced for optimization of memory consumption. The ability to destroy Fragment’s View hierarchy allows to free some memory while the Fragment is e.g. not visible. But then the question becomes: Fragments implement support for the standard save & restore flow, why did Google invented yet another mechanism for essentially the same purpose?
I don’t know. What I do know, however, is that even FragmentStatePagerAdapter doesn’t rely on this optimization and forces Fragments to undergo complete save & restore.
As far as I’m concerned, this entire mechanism is a stunning demonstration of the dangers associated with premature optimization. It wasn’t really required, it’s not being useful and it drives the complexity of using Fragments to the astronomic heights. To me, it looks like all the additional issues that we experienced with Fragments over the years originated from the “root of all evil”:
Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.
Knuth, Donald
And the sad irony is that it looks like Google developers themselves don’t understand Fragment’s lifecycle anymore.
Google released LiveData Architecture Component with a serious bug described by Christophe Beyls in this post. If there would be just one developer working on this feature who would really understand Fragment’s lifecycle, that problem would become evident at design stage. It took Google several months to fix this bug. Lastly, during Google IO 18 they announced that the issue was fixed. The fix was… introduction of yet another lifecycle for Fragment’s View hierarchy.
So, if you’re using LiveData Architecture Component, now you’ll need to keep in mind that there are two distinct lifecycles associated with Fragment object.
Conclusion
Fragments are a mess. Their lifecycle is rocket science. The only thing worse than Fragments is their official documentation. Google developers don’t fully understand Fragment lifecycle by themselves and continue to increase the complexity of this beast.
Said all that, I’ve been using Fragments for years and will continue to use them in the foreseeable future. Compared to “one Activity per screen” approach, Fragments can provide better user experience and make the application feel snappy. To use Fragments and keep my sanity, I’m using the approach to Fragment’s lifecycle described in this article. It probably doesn’t cover 100% of use cases, but it worked great for me in the past years.
There are two advantages to this approach: it makes the handling of Fragment’s lifecycle very similar to Activity’s lifecycle and independent of it. You might’ve noticed that nowhere in the post I mentioned the state of the enclosing Activity. As long as I stick to this scheme, I don’t care what happens to that Activity. I don’t even care whether this Fragment is top level or nested one.
So, I override minimal number of methods and don’t have inter-dependencies between lifecycles. This makes the complexity of Fragments manageable for me. If you have anything to add or want to point out any mistake in this article – feel free to do it in the comments section below.
On question. If that many Android developer won’t use Fragment is any circumstances, how could they implement BottomNavigation or ViewPager?
Hello Bagus Aji Santoso. Thanks for your question.
ViewPager is not tied to Fragments in any way. There are Adapters that allow to use Fragments with ViewPager, but you can also have an Adapter that uses standard Views. The same goes for bottom navigation.
There is a framework named Conductor which aims at providing navigation and backstack capabilities with just standard ViewGroups. I’ve never used it myself though.
I also saw implementations of bottom navigation where different destinations corresponded to different Activities. Can’t say I like this approach, but it’s still an option.
Hi Vasiliy. You didn’t mention onViewCreated(view: View, savedInstanceState: Bundle?) method at all.
Isn’t this a good place to initialize your Views?
P.S. Have you considered writing an article on Services? Woild be awesome
Hi Andrew,
I didn’t mention very many methods from Fragment’s lifecyce, not just this one. That’s why I like this approach 🙂
Seriously though, I don’t see a reason to override onViewCreated, but maybe I’m missing something. Is there a use case for this method that can’t be covered in onCreateView or onStart in your opinion?
As for the Services, I have this article about bound IPC services. It’s a niche article because IPC services are kind of exception and it needs an update too, but maybe you could find something useful there.
When I think about it – a comprehensive post about services might actually be a good idea. I know Services are not that widespread, but when you do need to implement one it’s tricky. Thanks for an idea.
Hey Vasiliy,
As for the onViewCreated method and kotlin, you can omit views’ initialization at all and use them by their Id (with the help of kotlin synthetic ofc).
Well, I personally think that neither Activities nor Fragments should be aware of UI implementation details. That’s why I always strive to abstract all UI logic inside MVC views. Therefore, this use case is irrelevant for me personally.
However, even if you put UI logic in Fragments, I would say that adding one more lifecycle callback thus complicating the design to spare several lines of trivial code is not the best trade-off to make, but that might be a bit subjective.
Thanks for post. No mention of ‘onActivityCreated’ method and ViewModel handling ?
Hey,
In my opinion, onActivityCreated is one of the worst mistakes in Fragment’s API because it “leaks” Activity lifeycle into Fragment. This goes against the fundamentals motivation behind Fragmnets as stated by Dianne Hackborn (quoted at the beginning of this post).
That said, if you do know a use case for this method, please share it for all of us to learn. As I said, it’s very improbable that “my” approach covers 100% of use cases.
As for ViewModel, I already stated my opinion about this library. I don’t plan to invest time into explaining how to implement bad practices. I hope it doesn’t sound too harsh. I really don’t want to promote something that I see as a mistake.
FWIW, Android’s official “Guide to App Architecture” reads the savedInstanceState and grabs the view model reference (initialising it with the state) in onActivityCreated() https://developer.android.com/jetpack/docs/guide#building_the_user_interface
Hello, Vasiliy. As usual, you’ve shared a really enthralling post. These fundamentals really give more room and understanding for a newbie like me. This post turned out easier to read after Activity’s Lifecycle. However, I do have some questions or rather a sort of clarifications I would like to receive about lifecycle.
1) Usually, I use onAttach method to cast activity into Listeners for the fragment. But in this article, you say that “the casting of Activities into listener interfaces also takes place in onCreate”. I prefer using onAttach method as it seems to me more relevant abstraction to cast activity (We’ve just attached to it so let’s make friends with parent activity and get references to a listener). Do you think that onAttach is redundant and has the same capabilities as onCreate?
2) You say that “The rule for View onCreateView(LayoutInflater, ViewGroup, Bundle) is that every Fragment’s field related to View hierarchy must be assigned in this method. This includes list Adapters, user interaction listeners, etc. “. However, according to your logic, the best place to set up listeners is onStart method and I do prefer this kind of flow. Could you please shed some light on this slight contradiction?
P.S. I would like to subscribe to your bugreport in the issue tracker, but see no CC to me there. Maybe it’s because the bug is in the blocked status ATM?
Hi Dmitriy,
Thank you for these interesting questions.
1)
We all learned to cast Activities into listeners in onAttach because that’s what official documentation demonstrates. However, if you think about it, the only reason this method exists at all is because Fragments can be retained.
For example, retained Fragments are not re-created upon configuration change and can be attached to new Activity without onCreate being called at all. Therefore, if you’d do this casting in onCreate of retained Fragment, you’d get strange errors and leak the first Activity that retained Fragment happened to be attached to. Not good.
However, as I wrote in the article, I don’t think that retained Fragments should be used at all. If you don’t use them, it becomes safe to cast Activity in onCreate. So, it’s safe, but why not do it in onAttach?
There is absolutely nothing wrong with doing it in onAttach. It will work and you can do it if you find it cleaner. However, my goal, when it comes to Fragment lifecycles, is to simplify the logic and make it as similar as possible to Activity counterpart. Overriding onAttach just for the sake of casting Activity seems like unnecessary complication of Fragment’s lifecycle to me.
2)
You’re right – this part might read like contradiction. I’ll make it clearer.
What I meant is that if you store references to user interaction listeners in Fragment’s fields (for whatever reason), then you should re-initialize these fields in onCreateView. However, the registration of these listeners that you initialize in onCreateView should still be delayed to onStart. In cases when you use anonymous classes as listeners, it’s alright to just define them and register at the same time in onStart.
Unfortunately, I don’t know why you can’t star the issue in the issue tracker.
I have a question. If I want to restore for example the scroll position of a list, I can’t do it in onCreate, because there’s no view yet. In this case i can only restore the previous state in or after onCreateView, so there’s the risk to restore a previous and invalid state if onSaveInstanceState hadn’t called before the fragment’s view hierarchy destroyed. Am I right? In this case what can you suggest? Should I risk restoring this invalid scroll position?
Hi Mike,
To the best of my knowledge, just like with any other View, Android restores the state of ListView/RecyclerView by itself. Since this hasn’t been the issue with any views so far, I’d say that it’s safe to assume that Android handles it properly.
That’s basically the bug that I opened in Google’s issue tracker – I don’t believe that the documentation of Fragment’s lifecycle is correct.
Therefore, if you’re not going to bind a new data to lists – there is nothing for you to do.
If you’re going to re-bind the data to lists, then you’ll do that in onCreate() (or, at least, you’ll initiate the async process in onCreate()). If you’d like to keep the scrolling position, then you will need to use the standard approach.
To summarize. If you restore the state in onCreateView(), then, according to current documentation (which I believe to be incorrect), you might run into a problem. Therefore, don’t do that. If you want to keep track of list scroll position in the Fragment – store it as Fragment’s field.
If you’re using RecyclerView, then the specified LayoutManager is responsible for saving/restoring state (scroll position). The official LinearLayoutManager, GridLayoutManager etc support this, but if you’re using a custom layout manager from somewhere, then you’ll need to check that.
Thanks you so much for great articles on both activity and fragment life cycles. I’ve read this article deeply for 4-5 times. I have a question similar to Arcfej’s question but with different implications.
-Suppose I have a custom view that does not handle save and restore and I am responsible for saving and restoring its state.
-Since we have established we can only restore sate in onCreate(). I have no other option but to maintain a copy of its state as fragment’s field. And re create this view in onCreateView(Layout….) everytime using this fragment field.
But the problem is – In which life cycle method should I copy this state from inside the view and save it to my fragment’s field?
onPause seems like a bad candidate because fragment is still subscribed to events and view’s state may change based on those events.
onStop seems like a bad candidate because onSaveInstanceState may be called before onStop and may again save stale state.
1-It seems to me that for any guarantee of state not being stale onSavedInstanceState() has to be called after onStop() is my thinking correct?
2-Where do you recommend I save state for such a view and what is your reasoning behind it?
Hello Tanmay,
In general, there should be no state in the view (except for the UI state stored automatically inside Android View subclasses). Therefore, your best bet is to keep any state (for example: the option chosen by the user) in the controller to begin which. Then you restore this state in onCreate() and bind it to the view in onStart().
However, in some cases, this might be a bit of PITA. Therefore, occasionally, I add saveState(Bundle) and restoreState(Bundle) methods to views and call them from onSaveInstanceState() and onCreateView() respectively.
Simply the best Activity&Fragment lifecycle guide I’ve ever read! Thank you very much Vasiliy! Keep up the good work! Your posts and videos are of great value to android developers.
Thank you. Glad to hear it was helpful!
Hi Vasiliy,
Excellent pair of articles on fragments/activities – the best explanation and pragmatic handling of lifecycle subtleties by far! Regarding fragment onDestroy(), I understand that you recommend to not override it. I was wondering if your recommendation still stands for a multithreading case. In my app I need to have a HandlerThread to offload some periodic heavy duty computations from the UI thread. The computation is related to an object that is instantiated in fragment onCreate(). The fragment views are updated by the UI thread Handler when computation is complete. I was planning to setup this thread in onCreate() (coupling it with the object it manipulates) and terminate/kill it in onDestroy(). The object has to be instantiated in onCreate() as its state is saved/restored via a Bundle. But I am little bit reluctant to setup/terminate the HandlerThread in onStart()/onStop() – I will need to make 100% sure that the old thread is completely gone (in onStop()) before letting a new thread (in onStart()) play with the same object? Is onCreate()/onDestroy() justified in this scenario? Could you recommend a better approach?
Thanks,
-Ed.
Hi Ed,
What I do in such cases is simply encapsulate the “computation logic” (whatever it happens to be) in standalone objects and make them observable. The multi-threading becomes internal implementation detail and Activities/Fragments aren’t even aware of the fact that multiple threads are involved. They just subscribe in onStart() and unsubscribe in onStop(), and then fire computations as needed.
You don’t usually need to cancel these computations or perform any cleanup – in most cases it’s enough to unsubscribe from notification and that’s it. The computation will complete, but its results will be discarded. However, you can also add cancel() method to that object and call it in onStop() if there is hard requirement to stop the computation when the app is in background.
So, in this case, I’d say that you don’t need onDestroy() either.
Hope this helps.
Hello Mr. Vasiliy,
I’m a big fan of yours. I’ve read and applied your practice about activity / fragment lifecycle and it works like champ. However, there is a point that I’m slightly disagreed is that
“set all the aforementioned references to View fields to nulls in onDestroyView()” is premature optimization.
The problem is that when you keep adding fragments to a backstack, OMM will happen. In order to resolve this issue, you must effectively remove fragment reference and there is no way to remove a fragment reference from FragmentManager but calling popBackStack() or popBackStack(@Nullable String name, int flags). These methods always come with back animations which is not a thing that we always want (there are cases we only want to silently remove fragment references without UI notice).
So actively null out all reference of view hierarchy is a must to mitigate the issue.
Hi,
Glad this technique worked for you.
If you run into OOM, then, sure – optimize your code. Once you know for sure that optimization is needed, it’s no longer “premature”. That said, absolute majority of apps will never ever reach that point.
Consider this: there are many apps out there which use Activity-per-screen approach. Activities are much “heavier” than Fragments and their view hierarchies never destroyed. Still, applications that use this approach work alright and don’t crash with OOMs left and right. Why? Because it’s not a general problem and it might affect a tiny minority of apps.
Therefore, if you try to optimize for that corner case until you know it’s relevant to your app, you’re engaging in premature optimization IMHO.
Hi! Thank you for this awesome article.
I have a question, how do you deal with states that are shared across multiple screens? For instance, I have fragmentA, fragmentB, fragmentC, and they are arrange accordingly in the stack.If a data is updated in fragmentC, what is the correct to way to update the same data that is present in fragmentB and fragmentA?
My current approach is publishing a simple event that fragmentA and fragmentB subscribes to. However, I currently do the subscribing part in onCreateView and the unsubscribing part in onDestroy.
Hi Edwin,
Using pub-sub like you do is one way to handle this task. Alternatively, you can simply have a stateful object that lives in Application scope which is shared among all Fragments.
However, if you subscribe in onCreateView and unsubscribe in onDestroy, then it’s surely non-optimal. You should use compatible methods. I recommend onStart()/onStop(), unless you have very special reason to choose another lifecycle.
If you need to make sure that Fragments receive events even when they’re backstacked, then onCreate()/onDestroy() should do.
Hi Vasily,
Thanks for such a clear and helpful couple of articles on one of the core components of Android development. I just finished reading them once and will go through them a few more times.
Going to try and use these principles for apps I write from now on.
Hi Vasily,
first of all nice article, thanks for sharing.
I’ve a small doubt, you’re adding items in the adapter into onCreateView, but how you can add them if the view isn’t rendered ?
Could it be an issue ?
Thanks
Hello Enrico,
I don’t add items into adapter in onCreateView. Whatever initialization List/RecyclerView requires, I’ll usually do that in onStart().
Vasiliy
Hi 🙂
You are the best, you explain in wonderful way !
I have one question:
– i’m developing a simple dialog fragment, and I expected that I have to set the dialog.window dimension inside onCreateView, but it doesn’t work.. to be work I have to set those dimensions inside onStart() method. Why ? 🙂
Hello Gabriele,
I do the same, but never actually was curious enough to understand why. For some things in Android I just accept “because it’s Android” as an answer 😉
Thanks for your article
To save fragments UI state is it good practice to keep a reference of the view in a variable like this?
lateinit var rootView: View
final override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if (rootView == null) {
rootView = // Inflate layout and
}
return rootView
}
Doesn’t it couse any memory issues sepcially in sigle fragment apps with many fragments?
Hey Hossein,
I don’t do that, but there is no major problem with this approach. In fact, Ian Lake even recommended this for performance reasons in some discussion (don’t remember where it was).
In general, I wouldn’t call it “good practice”, but it’s also not “big no-no”.
You mean, keeping a reference of the view will increase performance?
Potentially, yes.
In practice, in my opinion, if you ever need this “performance”, it’s a code smell. Layouts should inflate very quickly and you shouldn’t need to resolve to these hacks. If you do, then, first of all, investigate why you have this performance problem to begin with. Chances are that this hack won’t cover all of the aspects of that problem.
I’ve never used this in any of my apps to date.