My application contains two fragments.
My Android manifest file contains the following line:
"android:configChanges="orientation|screenSize"
which I need for the second fragment. I don't want its onCreateView() or other method to be called an orientation change.
But my first fragment needs onCreateView() to be called when orientation changes. Since the manifest file contains the above line, the onCreateView() method is not called. Can anyone help me in sorting out this to make onCreateView() to be called when orientation changes for specific fragment?
I believe setRetainInstance() is the function you need.
From documentation:
Control whether a fragment instance is retained across Activity
re-creation (such as from a configuration change). This can only be
used with fragments not in the back stack. If set, the fragment
lifecycle will be slightly different when an activity is recreated:
onDestroy() will not be called (but onDetach() still will be, because
the fragment is being detached from its current activity).
onCreate(Bundle) will not be called since the fragment is not being
re-created.
onAttach(Activity) and onActivityCreated(Bundle) will
still be called.
Related
I find Fragment#setRetainInstance(true) confusing. Here is the Javadoc, extracted from the Android Developer API:
public void setRetainInstance (boolean retain)
Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change). This can only be used with fragments not in the back stack. If set, the fragment lifecycle will be slightly different when an activity is recreated:
onDestroy() will not be called (but onDetach() still will be, because the fragment is being detached from its current activity).
onCreate(Bundle) will not be called since the fragment is not being re-created.
onAttach(Activity) and onActivityCreated(Bundle) will still be called.
Question: How do you as a developer use this, and why does it make things easier?
How do you as a developer use this
Call setRetainInstance(true). I typically do that in onCreateView() or onActivityCreated(), where I use it.
and why does it make things easier?
It tends to be simpler than onRetainNonConfigurationInstance() for handling the retention of data across configuration changes (e.g., rotating the device from portrait to landscape). Non-retained fragments are destroyed and recreated on the configuration change; retained fragments are not. Hence, any data held by those retained fragments is available to the post-configuration-change activity.
It's very helpful in keeping long running resources open such as sockets. Have a UI-less fragment that holds references to bluetooth sockets and you won't have to worry about reconnecting them when the user flips the phone.
It's also handy in keeping references to resources that take a long time to load like bitmaps or server data. Load it once, keep it in a retained fragment, and when the activity is reloaded it's still there and you don't have to rebuild it.
Added this answer very late, but I thought it would make things clearer. Say after me. When setRetainInstance is:
FALSE
Fragment gets re-created on config change. NEW INSTANCE is created.
ALL lifecycle methods are called on config change, including onCreate() and onDestroy().
TRUE
Fragment does not get re-created on config change. SAME INSTANCE is used.
All lifecycle methods are called on config change, APART FROM onCreate() and onDestroy().
Retaining an instance will not work when added to the backstack.
Don't forget that the above applies to DialogFragments as well as Fragments.
The setRetainInstance(boolean) method is deprecated, use ViewModels instead.
The setRetainInstance(boolean) method on Fragments has been deprecated as of Version 1.3.0 of fragment API.
With the introduction of ViewModels, developers have a specific API for retaining state that can be associated with Activities, Fragments, and Navigation graphs. This allows developers to use a normal, not retained Fragment and keep the specific state they want retained separate.
This ensures that developers have a much more understandable lifecycle for those Fragments (one that matches all of the rest of their Fragments) while maintaining the useful properties of a single creation and single destruction (in this case, the constructor of the ViewModel and the onCleared() callback from the ViewModel).
I was reading Handling Configuration Changes docs.
The document advises to use Fragments that have setRetainInstance set to true and then recover the fragment via the fragmentManager's findFragmentByTag method.
My question is that when the activity is destroyed will the fragmentManager survive that? Is it like sharedPreferences where the values stored in it are unaffected by what happens in the activity as long as the values are committed?
No. When you set setRetainInstance(true) within a fragment's onCreate, (with fragment tag, say "my_fragment"), when your parent activity orientation changes, the android framework stores the instance for the fragment as long as the activity is not destroyed. When you save the fragment tag variable in the parent activity and restore it (see example: https://stackoverflow.com/a/47823139/7152359), you can again call getSupportFragmentManager()... and set the fragment using the tag "my_fragment" that you stored.
In simple words, setRetainInstance(true) is only used to help developers not go through complex/long procedures of onSaveInstanceState(..) and onRestoreInstanceState(..) like many a times developers have to do for activities.
From my investigations today I believe that FragmentManager does actually survive activity destruction due to configuration changes. This is because if you add a fragment with a tag to the fragment manager (for example, fragmentManager.beginTransaction().replace(R.id.container, myFragment, "blah").commit()), then you can still retrieve that fragment with fragmentManager.findFragmentByTag("blah") even after the activity has been destroyed and re-created due to a configuration change. You can see from the source code that it retains a ArrayList<Fragment> mAdded which contains all of these previously added fragments. If the FragmentManager was destroyed and re-created, this mAdded would have become an empty list, which clearly isn't the case as above.
HOWEVER, the nature of what is retained within each fragment depends on setRetainInstance. If you don't set retain, then only the fragment arguments and saved instance state are persisted, and the fragment instance is re-created by the framework. If you do set retain, then the whole instance (including fields) is persisted. However either way, the FragmentManager itself is still persisted otherwise we wouldn't be able to retrieve tags from it anymore.
While the persistence of FragmentManager across configuration changes is not explicitly mentioned by the documentation, I believe it's implied by statements such as the following:
during a configuration change, your activity and all of its fragments are destroyed and then recreated with the most applicable Android resources. The FragmentManager handles all of this for you. It recreates instances of your fragments, attaches them to the host, and recreates the back stack state.
i.e. if the FragmentManager was not persisted across the configuration change, it could not continue to manage this after activity re-creation.
Everywhere it is written that fragments have their own lifecycle . Also fragment life cycle depend on activity's lifecycle.What is the meaning of fragment's own lifecycle if it is dependent on activity's lifecycle?
First of all you need to understand what are lifecycle methods are and when are they called/invoked. Lifecycle methods are basically invoked at the different state of your Activty/Fragment. For example when you first launch your activity the following flow of events/methods are called depending upon the state of your activity. For example : When your activity is first launched OnCreate is called, when your activity is no longer visible then onStop is called. So basically you first need to learn at which state are these different activities called.Below is a great referential flowchart for the same.
Activity lifecycle methods :
Fragment lifecycle methods :
Now, when you create a fragment it is inflated into the activty. And it has its own set of lifecycle events/methods which are called and since the fragment is inflated into the activty when the state of your activity changes it effects the fragment and correspondingly different lifecycle methods of the fragments are called. Below is another pictorial representation of the relation between the lifecycle methods of the activity and the fragment.
Image source : Google Images
The activity lifecycle is fairly simple in comparison to the fragment lolcycle (image from Square's Advocating against Android Fragments)
In creation of a fragment, I encountered getActivity() to be null.
So to narrow down the problem, I kept a local copy of activity in onAttach(Activity activity), which by definition is when it is attached to an activity.
However, I logged the activity in onAttach, and it is still null.
I'm only running into this problem in 2.3.6 and below.
Is this a known problem with support package?
The series of methods called to bring a fragment up to resumed state are:
onAttach(Activity) called once the fragment is associated with its activity.
onCreate(Bundle) called to do initial creation of the fragment.
onCreateView(LayoutInflater, ViewGroup, Bundle) creates and returns the view hierarchy associated with the fragment.
onActivityCreated(Bundle) tells the fragment that its activity has completed its own Activity.onCreate().
onViewStateRestored(Bundle) tells the fragment that all of the saved state of its view hierarchy has been restored.
onStart() makes the fragment visible to the user (based on its containing activity being started).
onResume() makes the fragment interacting with the user (based on its containing activity being resumed).
The bold method should be the one where getActivity doesn't return null anymore.
the onAttach method should not be used to call methods of the activity object, It should be used to initialise callback interfaces. An example of these interfaces can be found here.
This problem is because of the support package it means the fragment are from android 3.0 and up that is API level 11 and UP so for sure you will face app crash for android 2.3.6 gingerbird
this.getActivity();
I have an activity which contains 2 fragments. I want to save certain state of the activity and also of the fragments, in order to restore if the activity, or the fragments, are destroyed.
So I'm using onSaveInstanceState both in the fragments and in the activity, and take the data of the bundle passed to onCreate or onCreateView.
This works well besides, when the activity is destroyed. Then in restores it's own data, but, since in onCreate() I instantiate the adapter and the fragments again, they get no state.
How do I solve this?
Thanks in advance.
Most likely the cause of this, is that the Fragment's onCreateView() runs before the Activity's onResume() according to the Fragment lifecycle documentation:
http://developer.android.com/guide/components/fragments.html#Creating