Am I missing something or do Fragments not have a onRestoreInstanceState() method? If not, how do I go about attaining something similar?
Fragments do not have an onRestoreInstanceState method.
You can achieve the same result in onActivityCreated, which receives a bundle with the saved instance state (or null).
Check the source code here.
I know, that you have accepted answer, but you should read the official documentation about fragments, and it says (paragraph "Handling the Fragment Lifecycle"):
You can retain the state of a fragment using a Bundle, in case the activity's process is killed and you need to restore the fragment state when the activity is recreated. You can save the state during the fragment's onSaveInstanceState() callback and restore it during either onCreate(), onCreateView(), or onActivityCreated()
So, you can use that suits you best: onCreate(), onCreateView(), or onActivityCreated()
In Fragments guide's ListFragment example you can find:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}
Which you can use like this:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
// Restore last state for checked position.
mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
}
}
onActivityCreated() is invoked after the fragment returns back from the stack.
onViewStateRestored of Fragment is the equivalent of onRestoreInstanceState of Activity. But it is called after onActivityCreated(Bundle) and before onStart().
onActivityCreated is deprecated. and i found it just confusing in terms of the fragment lifecycle. just do this:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}
// and then:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
savedIInistanceState?.let{
//restore the data here
}
}
Related
I have a few CheckBox elements inside one of my Fragments.
Every time I leave this Fragment it seems to nor save or restore the checked state of each one provided by the user.
In the FragmentList example you can find:
CheckBox check1;
boolean active;
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("state1", check1.isChecked());
}
Which you can use later like this:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
// Restore last state for checked position.
check1.setChecked(savedInstanceState.getBoolean("state1"));
}
}
But somehow the CheckBox elements don`t save their state.
Is this the correct approach I should take?
Unless you're keeping your Fragment reference alive through the lifecycle of the application, it should be fine to save its state in onSaveInstanceState and restore it in onActivityCreated.
One important point, though, is also to save and restore that state in the Activity level by doing like:
public void onCreate(Bundle savedInstanceState) {
...
if (savedInstanceState != null) {
// Restore the fragment's instance
mFragment = getSupportFragmentManager().getFragment(
savedInstanceState, "fragKey");
...
}
...
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save the fragment's instance
getSupportFragmentManager().putFragment(outState, "fragKey", mContent);
}
Please check to see how your Activity is behaving in your scenario.
I have a fragment attached to the activity using XML (and setContentView() in activity). A have a problem because I have very dynamic views in my fragment, so when orientation changes
I must restore all states of views.
I have problem because I'm using something like that:
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("restore", true);
outState.putInt("nAndroids", 2);
}
But after orientation change when methods with param Bundle savedInstanceState are called (like onCreateView etc) my savedInstanceState is always null.
I'm not a noob in the Android but now I'm very angry because of this problem...
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null) {
//smth
} else {
// smthelse THIS IS NEVER REACHED BECAUSE BUNDLE IS ALWAYS NULL
}
getListView().setDivider(getResources().getDrawable(R.drawable.list_divider));
}
All the problem was in that I don't declare android:id for the fragment in XML. Android needs ID or TAG to recognize stored fragment and reproduce all elements in it. So guys, remember - every instance of fragment needs unique id or tag!
Also, when setRetainInstance(true) is declared then bundle should always return null.
I had a similar problem where I was always getting savedInstanceState as null inspite of supplying the bundle to the Fragment.
The only solution that worked for me was to do
myFragment.setArguments(bundle)
with my bundle from the Activity and do a
Bundle bundle = this.getArguments();
in onCreateView of the fragment.
Hope this helps someone else.
For Fragment :-
use this for save state of fragment on orientation.
onCreate(Bundle save)
{
super.onCreate(save);
setRetainInstance(true);
}
See this tutorial :-
http://techbandhu.wordpress.com/2013/07/02/android-headless-fragment/
For Activity:-
When you start your application, in onCreate, your bundle object is null, so you have to put a check like below and when you rotate your screen then onSaveInstance is called and your bundle object is initialized
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
if (savedInstanceState != null) {
boolean t = outState.getBoolean("restore");
int s = outState.getInt("nAndroids");
}
}
First you should put your data, then call super.onSaveInstanceState(outState);
public void onSaveInstanceState(Bundle outState) {
outState.putBoolean("restore", true);
outState.putInt("nAndroids", 2);
super.onSaveInstanceState(outState);
}
And be sure that activity has not nohistory property in AndroidManifest.xml or set it to false.
<activity
android:noHistory="false">
If you are trying to use outState to save the state and destroy the fragment by navigating to another fragment, it will not work, you have in this case to save your state permanently in either sharedPreferences or if it's big and you want to be more organized you can use any persistence lib like Room, Realm, ...etc.
When should use outState and savedInstanceState only to make Fargment/Activity survive config change(rotation for example) or processes being killed by the OS when the app is in background for example.
Ok I know this is an old post but I couldn't find the right answer for me here nor many other places, so I am posting how I fixed my case.
So My Fragment is inside an Activity. And I originally tried to save Bundle only in Fragment and retrieve it at onCreateView. However that was the problem.
I fixed this by initiating myFragment object in activity and put that object to activity Bundle at onSaveInstanceState(). Then retrieved it at onRestoreInstanceState(). I used getSupportFragmentManager().putFragment/getFragment. Then the savedInstanceState in fragment was no longer null.
In a application whenever a fragment loses focus (i.e. another activity / fragment is opened over it), it's onSaveInstanceState() is called.
Same is mentioned in developers guide.
I'm trying to use this approch to maintain the state of my fragment whenever it is resumed.
My intention was to call this bundle in onActivityCreated(), when the fragment is resumed.
Although onSaveInstanceState is called before fragment losing focus.
But, when onActivityCreated() is called it recives the Bundle savedInstanceState as null.
How can I fetch the data from the bundle.
Code:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
MyListAdapter adapter = new MyListAdapter(getActivity(),
R.layout.my_row, titles, icons, this);
setListAdapter(adapter);
if (savedInstanceState != null) {
// Never goes inside this condiiton.
// Restore last state for checked position.
mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}
Then it means that your activity and fragment were just paused and did not have to save their instance state (onSaveInstanceState() is not necessarily called).
If you want to make sure onSaveInstanceState() is called, select "Don't keep activities" in the device's developer settings. Then each time an activity is paused, the system kills it forcing the call to onSaveInstanceState() (except when you just press back of course)
I have a Fragment Activity with a FragmentTabHost. I add the fragments to the tab using the following code:
mTabHost.addTab(mTabHost.newTabSpec(tab1Name).setIndicator(tabIndicator1),
EventSettingsStep1Fragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec(tab2Name).setIndicator(tabIndicator2),
EventSettingsStep2Fragment.class, null);
When I switch to different tabs, I'd like to retain all the values (view state, etc) so that I have the same data when I switch back to the tab.
I overrode the onSaveInstanceState method & in there, I added values that I want retained to the bundle.
I ran through the methods being called and I have the following:
Switching from Tab1 to Tab2: Tab1:onPause then Tab2:onCreateView, Tab2:onResume
Switching from Tab2 to Tab1: Tab2:onPause then Tab1:onCreateView, Tab1:onResume
onSaveInstanceState is not being called.
Here is the code for one of my fragments:
public class EventSettingsStep1Fragment extends Fragment implements View.OnClickListener {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "onCreateView");
if (savedInstanceState != null) {
Log.d(TAG, "restoring onSavedInstanceState");
Gson gson = new Gson();
event = gson.fromJson(savedInstanceState.getString("event"), Event.class);
}
if (event != null) {
//set views
}
return v;
}
#Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
#Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
#Override
public void onSaveInstanceState(Bundle outState) {
Log.d(TAG, "onSaveInstanceState");
super.onSaveInstanceState(outState);
Gson gson = new Gson();
outState.putString("event", gson.toJson(event));
}
}
Why is onSaveInstanceState not being called? Is it only triggered through the FragmentActivity?
onSaveInstanceState is not being called because the framework simply reuses the already-existing instance of the fragment. onSaveInstanceState only gets called when the instance is about to be destroyed and then recreated. This happens for example when you rotate the display and force the hosting activity to be recreated.
onSaveInstanceState is also not called when you push a fragment on the backstack of a FragmentManager. You will have to restore the state from the already existing instance, which can be very annoying. See SO questions How can I maintain fragment state when added to the back stack? and Once for all, how to correctly save instance state of Fragments in back stack? for example.
Basically you will have to do what the answers to these questions suggest: continue using the values of your instance variables and do not rely on a saved instance state.
We are using Fragments and we don't need them to be automatically recovered when the Activity is recreated.
But Android every time when Activity::onCreate(Bundle savedInstanceState) -> super.onCreate(savedInstanceState) is called, restores Fragments even if we use setRetainInstance(false) for those Fragments.
Moreover, in those Fragments Fragment.performCreateView() is called directly without going through Fragment::onAttach() and so on. Plus, some of the fields are null inside restored Fragment...
Does anybody know how to prevent Android from restoring fragments?
P.S. We know that in case of recreating Activity for config changes it could be done by adding to manifest android:configChanges="orientation|screenSize|screenLayout. But what about recreating activity in case of automatic memory cleaning?
We finished by adding to activity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}
It suppresses any saved data on create/recreate cycle of an Activity and avoids fragments auto re-creation.
#goRGon 's answer was very useful for me, but such use cause serious problems when there is some more information you needs to forward to your activity after recreate.
Here is improved version that only removes "fragments", but keep every other parameters.
ID that is removed from bundle is part of android.support.v4.app.FragmentActivity class as FRAGMENTS_TAG field. It may of course change over time, but it's not expected.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(createBundleNoFragmentRestore(savedInstanceState));
}
/**
* Improve bundle to prevent restoring of fragments.
* #param bundle bundle container
* #return improved bundle with removed "fragments parcelable"
*/
private static Bundle createBundleNoFragmentRestore(Bundle bundle) {
if (bundle != null) {
bundle.remove("android:support:fragments");
}
return bundle;
}
I was having a problem with TransactionTooLargeException. So thankfully after using tolargetool I founded that the fragments (android:support:fragments) were been in memory, and the transaction became too large. So finally I did this, and it worked great.
#Override
public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("android:support:fragments", null);
}
Edit: I added it to the Activity. In my case I have one single Activity app and Multiple Fragments.
Those who got NPE with ViewPager when use this method described in the accepted answer, please override
ViewPager.onRestoreInstanceState(Parcelable state)
method and call
super.onRestoreInstanceState(null);
instead.
I removed the fragments in Activity's onCreate.
For an app with a ViewPager, I remove the fragments in onCreate(), before their creation.
Based on this thread: Remove all fragments from container, we have:
FragmentManager fm = getSupportFragmentManager();
for (Fragment fragment: fm.getFragments()) {
fm.beginTransaction().remove(fragment).commitNow();
}
Use this one for androidx
override fun onCreate(savedInstanceState: Bundle?) {
preventFragmentRecreation()
super.onCreate(savedInstanceState)
}
private fun preventFragmentRecreation() {
supportFragmentManager.addFragmentOnAttachListener { _, _ ->
savedStateRegistry.unregisterSavedStateProvider("android:support:fragments")
}
}
This worked for me
#Override
public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
outState.remove("androidx.lifecycle.BundlableSavedStateRegistry.key");
}
View hierarchy in not restored automatically. So, in Fragment.onCreateView() or Activity.onCreate(), you have to restore all views (from xml or programmatically). Each ViewGroup that contains a fragment, must have the same ID as when you created it the first time. Once the view hierarchy is created, Android restores all fragments and put theirs views in the right ViewGroup thanks to the ID. Let say that Android remembers the ID of the ViewGroup on which a fragment was. This happens somewhere between onCreateView() and onStart().