Android onSaveInstanceState and starting sub-activities. - android

I have an app which has a main activity that calls other activities on a button press.
The main activity hosts a class which contains a lot of data and is parcelable. the data is the backbone for the application and I need to save it across orientation changes. In order to achieve this it use onSaveInstanceState and put the parcelable object in the bundle.
However, I have noticed that onSaveInstanceState is called everytime i press a button and start one of the new activities as well as on orientation changes. This is slightly slowing down the transition to the other screens.
Is there a good way to only store this information when I know for definite that the activity will be destroyed rather than every time I navigate away from the Activity periodically. I understand that the minute I navigate away from the main activity there is a chance that it may get destroyed.
Thanks,
M<

For primitive values, you should use onSaveInstanceState. For restoring you can use onRestoreInstanceState or you can some code in onCreate like this:
if(savedInstanceState != null) {
// restore old state
} else {
// a fresh start
}
Now for restoring big objects like Bitmap etc if they are not expensive to create and doesn't make your UI sluggish, create them again on restore. If you do not want to that then use onRetainNonConfigurationInstance and code will look like this:
#Override
public Object onRetainNonConfigurationInstance () {
return bmp;
}
#Override
public void onCreate() {
bmp = (Bitmap)getLastNonConfigurationInstance();
}
WARNING: This api is deprecate, you might use it on old platforms. I put it here for illustration purpose. The new way to do this is more involving.
Here is detailed ref:
getLastNonConfigurationInstance
onRetainNonConfigurationInstance
Recommended soln for new api levels 11 and above:
"Use the new Fragment API setRetainInstance(boolean) instead; this is also available on older platforms through the Android compatibility package." snippet from the doc when you click on the links above.
So, you will create your bitmaps inside the fragment and call setRetainInstance on fragment so that fragment and its content (in your case bitmap) will be retained accross config change.
Ref:
setRetainInstance

Related

Android - Save and recreate instance of fragment

I'm new to android development, and I have come across this article, that shows how to restore the activity state : http://developer.android.com/training/basics/activity-lifecycle/recreating.html
And I was wondering if it is the same about fragments, do I need to implement
onSaveInstanceState and onCreate
the way they show, or do I also need to add something to the activity, since it is a fragment, and it doesn't work exactly like an activity.

Save Activity/Fragments state custom class

I would like to know if there is a method to Save a Custom Class while rotating in an Android app.
I want to save all instanced object called of the MatrixGame class...someone can help me?
Thanks
Since the Fragment lifecycle is independent - when you're using a Fragment you can set it so it doesn't get destroyed upon configuration changes.
As you noticed, the Activity class gets destroyed and re-created when you rotate the app (or apply other configuration changes), if you want to persist the Activity state you can use sqlite and save whatever you need in the onPause() method. Then in the onCreate() method check the DB for last known state.
If you want to avoid saving the state "forever" (meaning, the user turns off the app and tomorrow when she turns it back on - you want to start fresh and not load the last known state) you can add a timestamp and set a threshold which, if passed, the data is considered stale and gets disregarded.
Two comments:
As #saprvade wrote, Fragments gets destroyed by default, in order to prevent it you should call: setRetainInstance(true)
In case you want to implement #saprvade's other suggestion (have the activity ignore rotation changes) you can see the following explanation of how to implement: https://stackoverflow.com/a/456918/1057429
You should serialize your object. You can implement Parcelable or implement Serializable (Parcelable is several times faster). Then you will be able to put it in a Bundle in onSaveInstanceState and restore it in onCreate or onRestoreInstanceState.
Also, you can serialize your object to String, e.g. json string.
Another option would be to store your object in a database or a file. It depends on your needs.
If you don't want to recreate this object on screen rotation you can change the lifecycle of your Activity by adding a configuration change flag in AndroidManifest.xml. If we are talking about a Fragment, you can call setRetainInstance(true) to avoid fragment recreation on rotation.

Restoring the state of the activity

For restoring the state of the activity after it is recreated (for instance after the screen-orientation change) I implemented onSaveInstanceState() and onRestoreInstanceState(). It is simple to save/restore simple information like int, double etc. But what about saving/restoring objects like Timer?
You cannot store "live" objects (like db connections) in the Activity arguments or saved instance data. Those mechanisms are designed so that the application can be completely stopped, so it only works with things that can be "serialized" and later restored.
What you can do is use fragments. If you add a fragment without UI (check here, look for
“Adding a fragment without a UI”) and call on it setRetainInstance(true) the fragment will get reattached to the activity, surviving any configuration change.
Hope it helps. (Remember you can use fragments with old Android versions by using the support package)

Fragment has target not in Fragment Manager

I'm new to android programming.
I just try to save the state of the ListView in my fragment. For that I follow headless fragments (fragment which has no UI). In this fragment, I save the data, used in the ListView, and starting the headless fragment from the main fragment (the one which has the UI).
Now I got the exception:
java.lang.IllegalStateException: Failure saving state: RetainedFragment{4161f850 #1 work} has target not in fragment manager: JobOpeningFramgent{41601c00}
As far my concern, this is happening when I'm trying to replace the fragments with another one in the DrawerLayout.
Please temme the cause of this exception, for better understanding.
Thanks.
Boopathy.
Here's a workaround:
put this in the fragment that causes the problems:
#Override
public void onSaveInstanceState(final Bundle outState) {
setTargetFragment(null, -1);
...
and remember to set it to the real target fragment when you need it.
I'm not sure what do you want to save and where do you want to save it.
The official docs state that: "A Fragment represents a behavior or a portion of user interface in an Activity."
Using a Fragment as a container of another Fragment's UI state is generally a bad idea.
If you want to persist some values throughout the activity lifecycle (that includes screen rotations) just override onSaveInstanceState method. If you want to store some variables even after activity life-time use singelton class or Preferences, and if you want to store your values even after app life-time use SharedPreferences
Please elaborate on what do you exacly want

Android app resets on orientation change, best way to handle?

So I am making a basic chess app to play around with some various elements of android programming and so far I am learning a lot, but this time I am lost.
When the orientation of the emulator changes the activity gets reset. Based on my research the same thing will happen anytime the application is paused/interrupted, ie. keyboard change, phone call, hitting the home key etc.
Obviously, it is not viable to have a chess game constantly reset, so once again I find myself needing to learn how to fix this problem.
My research brings up a few main things, overriding the onPaused method in my Activity, listening for Orientation, Keyboard changes in my manifest (via android:configChanges), using Parcelables, or Serialization.
I have looked up a lot of sample code using Pacelables, but to be honest it is too confusing. Maybe coming back tomorrow with fresh eyes will be beneficial, but right now the more I look at Parcelables the less sense it makes.
My application utilizes a Board object, which has 64 Cell Objects(in an 8x8 2D array), and each cell has a Piece Object, either an actual piece or null if the space is empty. Assuming that I use either Parcelable or Serialization I am assuming that I would have to Parcelize or Serialize each class, Board, Cell, and Piece.
First and foremost, is Parcelable or Serialization even the right thing to be looking at for this problem? If so is either Parcelable or Serializable preferred for this? And am I correct in assuming that each of the three objects would have to be Parceled/Serialized? Finally, does anybody have a link to a simple to understand Parcelable tutorial? Anything to help me understand, and stop further headaches down the road when my application expands even further.
Any help would be appreciated.
in your manifest in the <Activity> tag, you can add android:configChanges="orientation|keyboardHidden", this will stop the activity from reloading and call onConfigurationChanged() instead when the orientation is changed or the keyboard is hidden.
If you need to make adjustments when either of these events happen, you can override onConfigurationChanged() in your activity, if not all you have to do is add the property to the manifest.
Something like:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.myLayout);
}
works perfectly well.
Androids default way of handling the events is to recreate the activity. Basically you handle one process correctly and everything works, no need to worry about handling those things manually.
The Application Fundamentals has a complete overview of the activity life cycle, but in short you want to save your activity state in the onSaveInstanceState() method and use the Bundle you get in the onCreate(Bundle savedInstanceState) to restore your application state.
If you want to store your classes in the Bundle your best bet is to implement the Parcelable interface. Then to save your state you do:
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putParcelable("yourObject", mYourObject);
}
and in the onCreate method you simply do:
if (savedInstanceState != null)
mYourObject = savedInstanceState.getParcelable("yourObject");
Of course you could just convert your objects into normal array representations that the Bundle can already contain and just skip implementing the Parcelable interface. Basically add a method toArray() to your object and a static method fromArray(). Well, play around and see which suits you better.
Or stick this line in your OnCreate so it doesn't roate. Problem solved.
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
Override onRetainNonConfigurationInstance in your Activity class.
In this method you must return an object, just bundle up your game's state in a single state object and return it in this method. Make sure this is only a state object, by that i mean it should have no handles to an activity, view, etc. contained within it or you'll get memory leaking.
In your onCreate method call getLastNonConfigurationInstance to get the object back.
You don't have to worry about the implementation details (the serialization) android takes care of that.
If you haven't already make sure you've set your Launch Mode in the manifest to either singleTask or singleInstance depending on which fits your needs better. By default if someone hits home and then comes back to your application it launches a new copy of the activity, if not handled or configured for single instance you'll get multiple copies of your game activity running.
When you save the board state, make an int[64] and at each position store the corresponding piece. So 0=empty, 1=white pawn, 2=white knight, etc...
When you load the board state, iterate through the array and create the appropriate piece objects at the appropriate locations.
You can convert the int[64] to a string to store in SharedPreferences, or use it with a Parcelable or whatever. Only store the minimum data you need.

Categories

Resources