I am using ActionBarSherlock, along with several MenuItems that I dynamically manage, depending on program state. Specifically, I save the MenuItems into instance variables in the Activity's onCreateOptionsMenu() method, and then can show and/or hide them as needed later in the Activity.
However, I found that the MenuItem variables can be lost on configuration changes, such as device rotation. So I also put them into a Parcel and pass them to onSaveInstanceState() for restoral through the savedInstanceState mechanism in the next onCreate() call, if needed.
This seems to work fine in the case of a device rotation. However, if I press the Home button, I get an error:
Unable to marshal value com.actionbarsherlock.internal.view.menu.MenuItemWrapper
Any idea what's the difference, or suggestions how to fix? Thank you!
UPDATE
I found a simple solution using onRetainCustomNonConfigurationInstance(). By overriding this method you can stash an object of a class of your choice when an Activity is being destroyed, and then retrieve it using getLastCustomNonConfigurationInstance(). This seems a lot simpler than messing with Parcelables, etc., and it works in my case.
But I'd still like to know: why do I get the above error using onSaveInstanceState()? And is it generally safe to use onRetainCustomNonConfigurationInstance(), or are there any gotchas?
Related
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.
I have a tabhost in my first activity and Im calling another activity sometimes; to handle some details. I need to remember the last tab selected in my first activity when the second one returns. Problem is the first activity is getting destroyed and created again and onSaveInstanceState is not getting called. The only option which I can think of is to write the last tab selected to external memory and read it back, which seems a little extreme considering this is just one tag. Is there any other option? I cant avoid the second activity (I had tried to change that to another fragment--fragment calling another fragment-- but that didn't work so well for me)
Cheers
If you want to save small amounts of data in Android, you can always use SharedPreferences. Here is a good example on how to use it.
And there are some other storage options for Android listed here
I'm having some trouble understanding how to make a simple DialogFragment to edit a (complex) object, say a Person, with first and last name, and a list of e-mail addresses each consisting of an enum (Work, Home, etc) and the address.
First of all, how do I properly pass the Person object to a DialogFragment? My current solution has a setPerson(Person person) method, that's called after my DialogFragment is created, but before dialog.show(). This works ok, until a configuration change happens (user rotates the screen). The DialogFragment gets recreated and the reference to my Person object is null. I know I can save the instance using onSaveInstanceState, but the object is complex and expensive, and persisting a large object this way seems wasteful.
I've also tried disabling configuration change in the activity that uses my dialog, and that fixes the problem, but I want the dialog to be reuseable and requiring all the activities that use it to disable configuration changes seems wrong.
Third option would be to save the reference to Person in a static variable, but again, I want the dialog to be reuseable and able to support multiple instances.
How do other people handle their expensive and complex objects in reuseable dialogs?
Well, there are several solutions, none of which are fantastic or failsafe if you are completely unable to serialize the object you're editing.
I don't recommend ever using android:configChanges="orientation" unless it's absolutely, 100% unavoidable. There are other configuration changes, and your app will still break with the others if you resort to using that solution.
But a simple solution that will work in the vast majority of cases is to call setRetainInstance(true) on the DialogFragment. This will prevent your Fragment from being destroyed and re-created on a configuration change. There is an edge-case where this might not work, though. There are other reasons besides configuration changes where the OS will attempt to put an activity or app 'on ice', for example to save memory. In this case, your object will be lost.
The cleanest way to pass a complicated Object to a fragment is to make the Object implement Parcelable, add the object to a Bundle, and pass the bundle to the Fragment with fragment.setArguments(bundle). You can unpack the Object in onActivityCreated() of the fragment by retrieving the bundle through a call to getArguments().
To persist the argument on configuration changes, simply save the "working" parcelable Object to the bundle provided by onSaveInstanceState(Bundle state) method of the fragment, and unpack the argument later in onActivityCreated() if savedInstanceState !=null.
If there is a noticeable performance hit from implementing Parcelable, or you have a "live" object of some kind, one option is to create a non-UI fragment to hold the data object. Without getting into details, you can setRetainInstance(true) on the non-UI fragment and coordinate the connection with the UI fragment through interfaces in the Activity.
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.
I read up on how Android handles "configuration changes" - by destroying the active Activity.
I really want to know from Android Team why this is. I would appreciate an explanation on how the reasoning went, because I don't understand it. The fact that it acts in that way puts us all, as I see it, in a world of pain.
Lets assume you have a Activity which presents a number of EditText:s, checkboxes etc. If a User starts to fill that form with text/data and then changes orientation (or get a Phonecall), then all input the User made is gone. I haven't found any way to preserve state. That forces us to make extremely painful coding to not lose all data.
As I see it, you need another "non-Activity" class (or "value-holding" class perhaps) that has one field for each "form element" (EditText, checkbox etc).
For every single "form element" that exists, you then need to attach an Event like "onChanged" (or onTextChanged or something like that) that updates the corresponding field in the "value-holding" class to make sure that for every single character you type (in a EditText for example) is saved at once.
Perhaps you can use some listener (like "onDestroy" or something) and then fill the value-holding class with data.
I have also found this piece of info where they talk about using Bundle, onSaveInstanceState and onRestoreInstanceState, but that also mean that the programmer has to manually save and then later put back the values in the correct place? This approach is a bit less messier than my suggestions above, but still not very nice.
Can someone tell me that I am totally wrong and that this is not how it works and that I totally missed some vital information?
You should read the Application Fundamentals (specifically, Activity lifecycle). Since Activitys must be able to handle being killed at any time due to memory contraints, etc. it's just a cleaner way to handle rotations without adding too much complexity - instead of checking every resource for an alternate resource, re-structuring the layout, etc. you just save your essential data, kill the activity, re-create it, and load the data back in (if you're willing to deal with the extra complexity of managing this yourself, you can use onConfigurationChanged to handle the configuration change yourself.) This also encourages better practices - developers have to be prepared for their Activity to be killed for orientation change, which has the (good) consequence of being prepared for being killed off by memory contraints also.
The contents of an EditText will be saved for you automatically when rotating the screen if you put an android:id attribute on it. Similarly, if you display dialogs using Activity#showDialog, then the dialogs are reshown for you after rotating.
on why part - short answer - because you might have resources that needed to be changed as you've rotated the phone. ( Images, layout might be different, etc )
On save - you can save you stuff to bundle and read it back.
#Override
protected void onSaveInstanceState(Bundle outState) {
String story_id = "123"
outState.putString(ContentUtils.STORYID, story_id);
}
or you can use onRetainNonConfigurationInstance () as described here
http://developer.android.com/reference/android/app/Activity.html#onRetainNonConfigurationInstance()
Finally if you don't have anything you want to handle during rotation - you can ignore it
by putting this into your activity in manifest
android:configChanges="keyboardHidden|orientation"
In general, i would read trough article from url above couple of times, until lifecycle is crystal clear.
#Alex's approach above pointed me to a really, really useful solution when using fragments:
Fragments usually get recreated on configuration change. If you don't wish this to happen, use
setRetainInstance(true); in the Fragment's constructor(s)
This will cause fragments to be retained during configuration change.
http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)