android: Saving activity state - android

I know this question has been asked many times but i have tried many solutions to no avail. sob sob.
Ill try to keep it simple. Im writing an app that uses spinners and edit texts to intake float numbers. so screen A has 4 spinners and 4 edit texts. I use a next button to open screen B
onClick(){
a.putExtras(sendScoreSoFar);
a.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(a);
}
Screen B is much the same with 3 spinners and 2 edittexts.
I can move from A to B and back to A while preserving the input from the user but when I go back to B again B is reset. I need to preserve the input so the user can flick back and forth to check input. I could use shared preferences but need to reset on the first loadup.
Ive tried various lines in the manifest:
<activity
android:name="com.package.Screen2"
android:label="#string/app_name"
android:alwaysRetainTaskState="True"
android:launchMode="singleInstance">
>
<intent-filter>...
and heres my on create and saved instance state code.
#Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
setContentView(R.layout.screen2);
super.onCreate(savedInstanceState);
setUpVariables();
if (savedInstanceState != null) {
int a = savedInstanceState.getInt("weightlostorprev");
spinner1.setSelection(a);
int b = savedInstanceState.getInt("metricorimp");
spinner1.setSelection(b);
int c = savedInstanceState.getInt("acute");
spinner1.setSelection(c);
etBigWeight.setText(savedInstanceState.getString("bigweight"));
etSmallWeight.setText(savedInstanceState.getString("smallweight"));
}
gotBasket = getIntent().getExtras();
passedResults = gotBasket.getFloatArray("the data");
passedWeight = passedResults[5];
Toast c1 = Toast.makeText(Screen2.this, "on create run! new run = "
+ newRun, Toast.LENGTH_LONG);
c1.show();
// question.setText(gotBread);
}
//#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
// Save UI state changes to the savedInstanceState.
// This bundle will be passed to onCreate if the process is
// killed and restarted.
int a = spinner1.getSelectedItemPosition();
savedInstanceState.putInt("weightlostorprev", a);
int b = spinner2.getSelectedItemPosition();
savedInstanceState.putInt("metricorimp", b);
int c = spinner1.getSelectedItemPosition();
savedInstanceState.putInt("acute", c);
savedInstanceState.putString("bigweight", etBigWeight.getText()
.toString());
savedInstanceState.putString("bigweight", etBigWeight.getText()
.toString());
}
//#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
int a = savedInstanceState.getInt("weightlostorprev");
spinner1.setSelection(a);
int b = savedInstanceState.getInt("metricorimp");
spinner1.setSelection(b);
int c = savedInstanceState.getInt("acute");
spinner1.setSelection(c);
etBigWeight.setText(savedInstanceState.getString("bigweight"));
etSmallWeight.setText(savedInstanceState.getString("smallweight"));
}
I know theres a simple solution, but i cant find it.
thanks in advance.
EDIT
as far as I can see you cant send data to onResume
so although the answers given were correct I solved the problem by removing all shared preferences code, removing the changes to the manifest:
android:alwaysRetainTaskState="True"
android:launchMode="singleInstance">
and instead of trying to preserve the activity, killing it as normal (with the back button)
and just send the values of the spinners and edit texts in a bundle using startactivityforresult.
So screenA starts screenB sending bundle a, then when screenB is killed with the back button, it sends bundle b back to screenA, then when screenA now starts screenB it Sends bundles a and b and b is populated with values in bundle b.
Probably no one interested but I thought id put up the solution anyway.
I can put up the code if anyone wants.
Thanks for the answers guys ill be taking a look at your options next time.

I am assuming you are moving back to A screen via the back button which is finishing your B screen.
You may want to consider putting each screen into a fragment, loading them both in your activity and using the fragment manager to flip between the fragments.
Here are a resources that will pertain to this solution.
Switching between Fragment view
http://www.vogella.com/articles/AndroidFragments/article.html#fragmentspersistence
You can use the fragments lifecycle call backs to handle the data and setRetainInstance() to "Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change)."
If you need pre honeycomb compatibility, check out the support libraries to do so.

Your choices are that you either persist the data (using, for example, SharedPreferences as you already know). Alternatively you could create a Service which holds your values. Both Activities bind to the Service, and can get and set the values from the Service. The Service will stay alive across your Activity transition, so the values set by ActivityA will be available to ActivityB.
Saving Activity state will only persist the state to the same Activity type. It is used when resuming an Activity, or persisting state across, for example, an orientation change.

Related

Bundle savedInstanceState always null in onCreateView() despite calling onSaveInstance()

I have FragmentA hosted by ActivityA. When the user selects an item from the options menu, ActivityB is started which hosts FragmentB. For now, I want to retain a String and a boolean from FragmentA by overriding onSaveInstanceState(), so when the user returns to FragmentA, their information is preserved.
Code from FragmentA:
#Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
//LOGS SHOW THAT THIS IS ALWAYS CALLED WITH CORRECT VALUES
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("string", "example");
savedInstanceState.putBoolean("boolean", bool);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.fragmentA, parent, false);
//LOGS SHOW THAT THIS IS ALWAYS NULL
if (savedInstanceState != null)
{
if (savedInstanceState.getString("text") != null)
{
mObject.setText(savedInstanceState.getString("string"));
}
bool = savedInstanceState.getBoolean("boolean");
}
...
}
From reading previous problems similar to mine:
1) I decided to place the code to recover the information in onCreateView() because onCreate() will not always be called. (Although tests with the code in onCreate() also have the same problem.)
2) I also did not call setRetainInstance(true), since this will cause Bundle savedInstanceState to always be null.
3) I made sure that the XML layout for FragmentA has an id. The various children elements of this layout also have ids.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragmentA"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
...
</LinearLayout>
Despite this, Bundle savedInstanceState is always null. What am I missing here?
Any help appreciated. Thanks!
Use the onPause() to save your persistent data, that's referring to data that you would like to keep permanently, and hence, you save it in your SharedPreferences or your Database. onSaveInstanceState on the other hand retrains the data in case an activity is destroyed and you'd like to get that data back, a good scenario would be a user filling in a form. You said in your example that you're navigating from Activity A to Activity B, then you go back to Activity A, when you first navigate, Activity A is not destroyed, it's only sent to the background, so when you return to it, it's already there and will be brought to the foreground, your values should actually be there unchanged, and onCreate and onCreateView will not be called as the Activity is still alive (although it might be killed in case of low memory on device).
Best and fastest way to test your onSaveInstanceState is the most often used scenario, Orientation Change, orientation change will cause the activity to get destroyed completely and re-created, so allow orientation change on Activity A, put some values in your saveStateBundle and rotate the device, now this will call all your methods from the start, onCreate, onCreateView, ... etc to create the activity with the appropriate layout, your savedInstanceState should not be null now.
Note this is assuming your application is staying alive, if you're going to close the app completely and still want to keep the data, then put your information in the SharedPreferences or Database and retrieve them when you start the app again.
// Edit 1
to show you how to store values in SharedPreferences anywhere in your application, these values are persistent even if your application is closed. (although onSaveInstanceState should be enough to what you're looking for but hope this helps)
// SharedPreference
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
// Storing
preferences.edit().putString("valueNameOrKey", StringValue).commit();
// Retrieving
preferences.getString("valueNameOrKey", defaultValueToReturnInCaseThatKeyIsNotFound);
// Edit 2
To remove any key/value pair from the SharedPreferences you can do this:
preferences.edit().remove("valueNameOrKey").commit();
But then pay attention to what happens when you retrieve the value, since the key will not be available, it's going to return the default value instead like this:
preferences.getString("valueNameoOrKey", ""); // "" Is my default value since I'm using a String
since you can use putString, putInt, putBoolean etc, same goes for the get functions, your default value must match the expected return type.
The savedInstanceState is null when no data was been previously saved. To save data you must override the onSaveInstanceStateBundle(Bundle) method as described in the Android documentation:
you should use the onPause() method to write any persistent data (such as user edits) to storage. In addition, the method onSaveInstanceState(Bundle) is called before placing the activity in such a background state, allowing you to save away any dynamic instance state in your activity into the given Bundle, to be later received in onCreate(Bundle) if the activity needs to be re-created. See the Process Lifecycle section for more information on how the lifecycle of a process is tied to the activities it is hosting. Note that it is important to save persistent data in onPause() instead of onSaveInstanceState(Bundle) because the latter is not part of the lifecycle callbacks, so will not be called in every situation as described in its documentation.
More info here

Getting null values when passing data from one activity to another in android

I am storing some data in static varibles in Activity1 and accessing in Activity3,and Activity 5. ie..
Activity1---> Activity2--->Activ3
.....................|
......................Activity4.-----> Activ5
This works fine if we close the application completely, from Activity1 (ie if the user is at Activ5 if he clicks back button then -->Activ4-->Activ2-->Activ1-->Exit)
But the user is exiting app at Activ3,4,5 by clicking Mobile exit button(Not the application exit), Now after few hrs the user is reopening application then , It(app) gets started from Activi3 or 4 or 5. (ie where ever app was closed).
Now, Since i am using some data(which i stored in static varibles in Activ1.)
I am getting null values. Why this is happining. How to avoid this types of errors.
I have used sharedpref to avoid this.Is this the only solution ?
Restore the state of activity when it is recreated, so that the values passed can be retrieved at a later time.
e.g. for an integer that was passed through intent do as following: -
//this will save the value if an activity is killed in background.
#Override
protected void onSaveInstanceState(Bundle outState)
{
getIntent().putExtra("count", getIntent().getStringExtra("count"));
super.onSaveInstanceState(outState);
}
//In restore instance state, retrieve the stored values. The following work can also be done //in oncreate, as when an activity is killed in background, onCreate method is also called.
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
if(savedInstanceState == null)
return;
int count = getIntent().getIntExtra("count", 0);
super.onRestoreInstanceState(savedInstanceState);
}
You need to add onSaveInstanceState methods to your earlier activities, and check the bundle received by the onCreate methods. Check out the Activity Lifecycle for details.
You should not store values in static members, the activity context gets released, thus you losing your static values. The preferred way of passing values between activities is using Bundles along with the Intents.
you can create new class and extend application ,and in that store all data that you want, its very useful but remember if you do that you must add name of your application in manifest file

Previous button clearing all data from my fields in android activity

i am developing an android application. i have 8 activities in the application. i have a submit button in the last activity. the user can go back to any activity and edit the data before pressing submit.i have two buttons in every activity named as Next and previous. and in the last activity an extra button called Submit is there. I want to come to any previous page from the next page and edit the data and go to submit.
Now my problem is, every time i press Previous my previous-activity data fields are becoming blank..
please help me solving this problem.
Save the state of your activities with bundles. The back button ends the lifecycle of an activity. Here's a link that would guide you through what you should do. Hope this helps.
http://developer.android.com/training/basics/activity-lifecycle/recreating.html
Dont kill your activity while moving to other activity. Also you can set a flag while launching activity whihc will just put your activity to backstack and will be retrieved when relaunch by pressing back. Follow below link for details:
http://developer.android.com/guide/components/tasks-and-back-stack.html
You need to save the state of Activity:
private static final String INPUT1 = "ip1";
private static final String INPUT2 = "ip2";
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putString(INPUT1, mEditText1.getText().toString());
savedInstanceState.putString(INPUT2, mEditText2.getText().toString());
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
And restore it when Activity starts:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Always call the superclass first
mEditText1 = findViewById(.....
mEditText2 = findViewById(.....
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
mEditText1.setText(savedInstanceState.getString(INPUT1));
mEditText2.setText(savedInstanceState.getString(INPUT2));
}
}
Also, you can tell EditText and other such Views to save their state individually, by adding android:saveEnabled="true" to their layout:
<EditText
android:id="#android:id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:saveEnabled="true"
android:inputType="text"/>

How to avoid getting the intent's extra to come back?

background
I have at least 2 activities on an app i've developing : a splash activity and a main activity. the splash activity calls the main activity.
on some cases (for now let's assume it's the first run only of the app) the splash activity adds a boolean extra (using intent.putExtra) to the intent to be true, and the main activity reads it using :
getIntent().getBooleanExtra(...,false);
the problem
i only wish to see the flag as true when i open the main activity after the splash activity.
This is why i've tried to just call (in the onCreate, right after i get the flag) :
getIntent().removeExtra(...);
another approach (acccording to this website) would be:
final Intent newIntent = new Intent();
setIntent(newIntent);
and another approach could be:
getIntent().putExtra(..., false);
none of those work: for some reason, on some cases, the flag is still returned as true.
as an example, i can press the home button (when the main activity was in the foreground), and then i start a heavy app (like the cut-the-rope game or a benchmark app), and then return to the app using the launcher.
in this case (which doesn't always occur), the splash activity isn't shown, but instead the main activity is shown. it calls onCreate again, while the flag itself is still set to true .
the question
why does it occur? how come the intent doesn't get reset?
is there a way to overcome this in an elegant way?
is it safe to just ignore the flag when the "savedInstanceState" is not null ?
It sounds like your VM is being torn down and re-started. I have observed on some devices that when the VM is re-created, instead of entering the launch activity, the last running activity is reconstructed from the saved state. It sounds like you are attempting to save state in the activity intent, so that is probably where your problems are coming from.
An important key to avoiding problems like this is to remember that the savedInstanceState bundle passed to onCreate is always null when the activity is being created new, in all other cases such as reconstructing after rotation or even when the VM has been torn down (as is likely your case) the savedInstance bundle passed to onCreate will be non-null.. my advice would be to use the intent to initialize your local state (the initial value of your flag) when the savedInstanceState is null, then never try to update your own intent, save your state in the savedInstance bundle..
E.G.
class Someactivity extends Activity {
private static final STATE_FLAG = "flagState";
private boolean someFlag;
protected void onCreate (Bundle savedInstanceState) {
final Intent intent = getIntent();
if(savedInstanceState==null) {
// Brand new, set from intent
someFlag = intent.getBooleanExtra(STATE_FLAG, false);
} else {
// Restored, set from saved instance
someFlag = savedInstanceState.getBoolean(STATE_FLAG, false);
}
}
protected void onSaveInstanceState (Bundle outState) {
// Save any updates to our activity state
outState.putBoolean(STATE_FLAG, someFlag);
}
}

Android: Saving contents of a listview onSaveInstanceState

Is there an built in way to save the contents of a listView as part of onSaveInstanceState to then restore later? I want the listView to look the same if the user hit the back button and now onCreate is being called again.
If you set your activity's launchMode to singleTask, then (unless the application was terminated / gc called upon) your data (list) will be preserved.
This way your device will hold only one running instance of your application at a time, so when you "launch it again" no matter from where, if it's already running in the background, then that instance will show up (with the latest data).
If there is a risk that your application was finished, and you still need the latest list of data to show up, this solution won't work.
But you could give a try to SharedPreferences: save the current data to the application's SharedPreferences, and restore it from there when launching it.
If it's ok, to have the predefined new list on each clean start of the application, but when getting it into foreground, you need the last seen items in your list, you should use the savedInstanceState parameter of your onCreate method:
private static final String MYLISTKEY = "myListLabels";
private ArrayList<String> listLabels = new ArrayList<String>();
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState.containsKey(MYLISTKEY))
{
listLabels = savedInstanceState.getStringArrayList(MYLISTKEY);
}
else
{
// TODO: populate explicitely your list
}
}
#Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putStringArrayList(MYLISTKEY, listLabels);
}
where listLabels contains the labels for your list.
It's not necessary for them to be of type String, you can put any type inside your Bundle.
When the user hits the back button the activity is always destroyed, so there will not be any restoring from savedinstance.
Android Training
When your activity is destroyed because the user presses Back or the activity finishes itself, the system's concept of that Activity instance is gone forever because the behavior indicates the activity is no longer needed.

Categories

Resources