I am trying to test onRestoreInstanceState method and when (exactly) it is called . So I have followed these steps :
start my activity. onCreate -- > onStart --> onResume were called .
press Home button on the emulator . onPause --> onSaveInstanceState --> onStop were called .
Click the icon in the launcher and launch my activity again. onRestart --> onStart --> onResume were called .
My java code :
package com.test.demostate.app;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
public class MainActivity extends ActionBarActivity {
private int visiters=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("TAG","onCreate");
}
#Override
protected void onPause() {
super.onPause();
Log.d("TAG","onPause");
}
#Override
protected void onStop() {
super.onStop();
Log.d("TAG","onStop");
}
#Override
protected void onStart() {
super.onStart();
Log.d("TAG","onStart");
}
#Override
protected void onRestart() {
super.onRestart();
Log.d("TAG","onRestart");
}
#Override
protected void onResume() {
super.onResume();
visiters++;
Log.d("TAG","onResume");
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("visiters",visiters);
Log.d("TAG",visiters+" visiters was saved ");
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
visiters=savedInstanceState.getInt("visiters");
Log.d("TAG",visiters+" visiters was restored");
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.d("TAG","onDestroy");
}
}
From the docs : Instead of restoring the state during onCreate() you may choose to implement onRestoreInstanceState(), which the system calls after the onStart() method .
So onRestoreInstanceState is called
after the activity was destroyed onPause --> onStop --> onDestroy then onCreate --> onRestoreInstanceState --> onResume( due to screen rotation for example )
after the activity was stopped onPause --> onStop --> onRestart --> onStart --> onRestoreInstanceState --> onResume( due to home icon pressing for example )
But why isn't it called after onStart ?
Thanks
After onStart() only if onSaveInstanceState() has been called.
From the docs:
This method is called after onStart() when the activity is being
re-initialized from a previously saved state, given here in
savedInstanceState. Most implementations will simply use
onCreate(Bundle) to restore their state, but it is sometimes
convenient to do it here after all of the initialization has been done
or to allow subclasses to decide whether to use your default
implementation. The default implementation of this method performs a
restore of any view state that had previously been frozen by
onSaveInstanceState(Bundle).
This method is called between onStart() and onPostCreate(Bundle).
Activity#onRestoreInstanceState()
The official documentation says about onRestoreInstanceState (Bundle savedInstanceState):
This method is called after onStart() when the activity is being
re-initialized from a previously saved state, given here in
savedInstanceState. Most implementations will simply use
onCreate(Bundle) to restore their state, but it is sometimes
convenient to do it here after all of the initialization has been done
or to allow subclasses to decide whether to use your default
implementation. The default implementation of this method performs a
restore of any view state that had previously been frozen by
onSaveInstanceState(Bundle).
This method is called between onStart() and onPostCreate(Bundle).
When the activity is beign re-initialized?
When orientation of the device changes your activity is re-initialized.
When there is another activity in front of your app and the OS kills your app for some reason, may be for example free resources.
Try change the orientation of emulator:
Ctrl+F12
Look the answer of user #GAThrawn
Pressing the Home button you leave your app and go to the home screen,
whilst leaving your app running in the background. This is a bit like
switching between windows on a Windows PC.
Except that when your phone is running low on resources like memory it
will start to close apps that are running in the background, so that
your phone has enough resources for what you're trying to do now.
Games are often amongst the first apps the phone will "kill" to save
resources as they often use a lot more memory and CPU than other apps.
This is why sometimes your game is still running paused, and sometimes
Android has closed it for you.
So I can not prove my second argument, since it decides it is the OS, at least do not know how to prove.
Related
The Android docs say that configuration changes can force an activity to be recreated, the most common change being a rotation. Now, there are some methods that can determine whether an activity is being destroyed to be recreated but all(?) of these methods are called after onStop() and aren't guaranteed or recommended for data saving purposes.
To give an example, there is an EditText activity which autosaves what they have written/updated if the user navigates away from the app via back button, app switch, e.t.c. However, the user might not want to save their changes when there is a configuration change so I need to be prepared for those cases.
When an activity is destroyed by the system because of configuration change onSaveInstanceState is called.
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
Store data that you want to persist in outState bundle.
Then you'll receive the stored data in onCreate and onRestoreInstanceState.
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
Use it to retrieve data you had stored in onSaveInstanceState earlier.
By default System saves the state of few widgets (EditText , TextView) on it's own and this magic happens in super.onSaveInstanceState().
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
So if you do not want to save the text in EditText , just do editText.setText("") before calling super.onSaveInstanceState().
Hope this helps.
Here is my setup:
Activity -> FragmentWithPages -> ViewPager{Fragment1, Fragment2}
From Fragment1 I launch a DialogFragment and then from the DialogFragment I launch activityForResult for an implicit camera intent to take picture.
Problem:
Sometimes, when returning from the camera my app crashes inside the onActivityResult of Fragment1. Why does this happen? Now understand the chain of callback of onActivityResult. It would be coming back in the order of Activity.onActivityResult -> FragmentWithPages.onActivityResult -> Fragment1.onActivityResult -> DialogFragment.onActivityResult. So my question is, why is DialogFragment null when I do mDialogFragment.onActivityResult(…)?
I imagine it might have to do with memory: the system kills my app and then restarts it after the Camera app returns. But if that were the case, why is the DialogFragment the broken link in the chain? What can I do to prevent this problem or handle it as if nothing went wrong?
So no I do not mean to simply catch the NPE, that does not really do much as it would void the whole purpose for which I took the picture.
When you return from a startActivityForResult(), there's no guarantee that the activity instance will be the same. The system is free to dispose of the activity that started for result, and reconstruct it to handle the onActivityResult() call.
If this happens, it means of course that any instance fields you had previously assigned will no longer be so. It's up to you to use onSaveInstanceState() and reconstruct your activity instance using the saved instance state Bundle passed in to your onCreate().
You can test this scenario by setting "don't keep activities" in developer settings on your device.
This is a common situation where the system destroys your activity because it needs more resources. You need to implement the onSaveInstanceState method and then restore the instance state in the onCreate or onRestoreInstanceState method.
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putExtra("yourData", yourParcelableData);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// keep the fragment and all its data across screen rotation
setRetainInstance(true);
if(savedInstanceState != null){
yourParcelableData = (yourParcelableData)savedInstanceState.getExtra("yourData");
}
}
There is a library that allows you to do it easier.
https://github.com/frankiesardo/icepick
Im really new to android and i have a little problem that i dont know how to solve.
Im having a small application that prints out the Activity lifes circles methods like this:
protected void onCreate(){
super.onStart()
print("onStart was called"); //this is a void and its only printing a text
}
protected void onStart(){
super.onStart()
print("onStart was called");
}
and so on...
While im i portrait mode the app is showing all the methods on the screen but when i switch to landscape the activity object is of course destroyed and it creates the first three methods again.
Im using onSaveInstanceState an onRestoeeInstaceState to try to save printed order on the screen while i switch from portrait to landscape.
How can i make it work?
example of app output in portrait mode:
onCreate was called
onStart was called
onResume was called
onPause was called
onStop was called
onRestart was called
onStart was called
onResume was called
i want theese prints to stay even if i switch to landscape.
This is onSaveInstanceState and onRestoreInstanceState i dont really know how to solve the problem here.
#Override
protected void onSaveInstanceState(Bundle savedInstanceState){
super.onSaveInstanceState(savedInstanceState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState){
super.onRestoreInstanceState(savedInstanceState);
}
If you go to the manifest I believe you are able to edit it to allow portrait and landscape displays and also have it recall the savedInstanceState. Sorry I can't give you a more detailed answer at the moment.
I have seen a few similar questions about onSaveInstanceState not getting called for Fragments, but in my case Fragments work fine, it's the main FragmentActivity that's having trouble.
The relevant code looks fairly simple:
public class MyFActivity extends FragmentActivity implements ActionBar.TabListener {
String[] allValues; // data to save
#Override
protected void onSaveInstanceState (Bundle outState) {
Log.d("putting it!", allValues.toString());
outState.putStringArray("allValues", allValues);
super.onSaveInstanceState(outState);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
allValues = savedInstanceState.getStringArray("allValues");
Log.d("getting it!", allValues.toString());
}
}
}
When pausing the activity (using the back button), the onSaveInstanceState is never called, and consequently, savedInstanceState is always null within the onCreate method upon resuming the app. I tried adding a block like this:
#Override
public void onPause() {
super.onPause();
onSaveInstanceState(new Bundle());
}
which was suggested in https://stackoverflow.com/a/14195202/362657 but while onSaveInstanceState then gets called, savedInstanceState remains null within onCreate method. What am I missing?
The issue here is that you are misunderstanding how onSaveInstanceState works. It is designed to save the state of the Activity/Fragment in the case that the OS needs to destroy it for memory reasons or configuration changes. This state is then passed back in onCreate when the Activity/Fragment is returned to / restarted.
In a Fragment, all of their lifecycle callbacks are directly tied to their parent Activity. So onSaveInstanceState gets called on the Fragment when its parent Activity has onSaveInstanceState called.
When pausing the activity (using the back button), the onSaveInstanceState is never called, and consequently, savedInstanceState is always null within the onCreate method upon resuming the app.
When pressing back, the user is destroying the Activity, and therefore its children Fragments, so there is no reason to call onSaveInstanceState, since the instance is being destroyed. When you reopen the Activity, it's a brand new instance, with no saved state, so the Bundle passed in onCreate is null. This is behaving exactly as designed. However, try rotating the device or hitting the home button, then you will see the Activity and its children Fragments have onSaveInstanceState called, and passed back in onCreate when returned to.
The hack you added, directly calling onSaveInstanceState(new Bundle()); inside of onPause, is a very bad practice, as you should never call the lifecycle callbacks directly. Doing so can put your app into illegal states.
If what you really want is for your data to persist beyond an instance of your app, I suggest you look into using SharedPreferences or databases for more advanced data. You can then save your persistent data in onPause() or whenever it changes.
In an update to the accepted answer:
A fragment's onSaveInstanceState may be called if you are using a ViewPager with a FragmentStatePagerAdapter (rather than FragmentPagerAdapter)
FragmentStatePagerAdapter
This version of the pager is more useful when there are a large number of pages, working more like a list view. When pages are not visible to the user, their entire fragment may be destroyed, only keeping the saved state of that fragment. This allows the pager to hold on to much less memory associated with each visited page as compared to FragmentPagerAdapter at the cost of potentially more overhead when switching between pages.
And don't forget:
When using FragmentPagerAdapter the host ViewPager must have a valid ID set.
Not an accurate answer to the question, but may help someone's day.
In my case, I called
#Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
}
I replaced the above code as below and things worked
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
}
In my app, I use the onCreate() method to initialize various variables. However, whenever I rotate the device, and the screen auto-rotates, onCreate() is called again, which re-initialize my variables. Is that how it's supposed to work? Where should I put code that I only want to be run once, when I start the app?
The above answers will lock your Activity into a specific orientation, which is generally not proper behavior for an Android app.
What you should be doing is storing your activity's state so when it gets recreated you can repopulate the UI with the stored values.
protected void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
// put your values in the Bundle
outState.putString("TextView1Text", textView1.getText()); // for example;
}
Then in your onCreate() method you can restore the values
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
textView.setText(savedInstanceState.getString("TextView1Text"));
}
}
This will also work when the user leaves the app via the home button or other means.
In the manifest put this line in all the activities that you want PORTRAIT
android:screenOrientation="portrait"