Best practices: Restore activity state [duplicate] - android

I'm a little confused about the Activity being destroyed and recreated when the user's device is rotated.
I've been reading around, and I understand the rationale for doing so (it basically 'forces' the developer to make sure they haven't 'missed' anything upon rotation/language/other changes)
I'm happy to respect best practice if it is seen as such, however it begs the question:
How do I 'remember' the state of the game/app, so that when the screen is rotated I have something from which to re-generate what the user was looking at?
From what I can see, absolutely everything is destroyed, the base class constructor runs and all variables in the Activity are 'null'.
I suspect the 'savedInstanceState' (Bundle class) is where I would collect that data, but reading around it only seems to be used for when the app is closed from lack of resources (and a few other extremely fringe cases)
Am I misinformed or misunderstanding the purpose of savedInstanceState? Is it wise to abandon best practice (letting the Activity be destroyed) if I'm mindful enough to not miss anything upon rotation? Thanks in advance for any advice.
I should note this question applies to game programming (I'm not using a layout XML)

Do you need your activity to be recreated? Is there work you want to do on rotation? savedInstanceState is where you would store data to be passed to the recreation of the Activity, but since you aren't using XML layouts you may consider just adding android:configChanges="orientation" to your activity in the manifest. This will give you manual control over what happens during rotation change.
In additional to my original comment, I would override config changes for keyboard being show as well:
android:configChanges="orientation|keyboardHidden"
If there is work you want to do on rotation change you can override onConfigChange and do anything you need there.
It's hard to say that there is a time when you should always override config changes, but I often do it when I have no dependency on resource folders that are size or orientation specific and I'm doing a lot of work that would take too much time to recreate.

There are other best practices to store data while activitiy config changes.1. Developer Doc Handling Runtime Changes2. Alex Lockwood : Handling Configuration Changes with Fragments, This will answer all your questions reagarding config change and storing data.
Impt:
Fragment file to store data. You must set setRetainInstace(true) in order to store data and retrieve while your activity config changes.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// retain this fragment
setRetainInstance(true);
}

Related

Why can't fragment save EditText state by default on orientation change?

I have a fragment, which contains many EditTexts.
And when I rotate the device the EditText goes blank. The fragment is not saving its state/value. But at the same time if I use that fragment layout for activity it stores the EditText's state.
And yes I've given the IDs to each EditText, even to each view if that matters.
I know I can use saveInstanceState to save those values but is there any other way to do it? Cause there are almost 20 EdiText in that fragment, so should I use saveInstanceState, will it be okay to save these many variables/values in saveInstanceState?
Update:
I was recreating the fragment in activity on orientation change, so that was the reason, EditText was unable to save its state.
Such a silly mistake!
So now I just used saveInstanceState like following:
if (savedInstanceState == null) {
initialiseNewTaskFragment();
}
And that's it. EditText is saving its state now.
Thank you JorgeGil for saving my time!
Well this is a fairly loaded question lol.
So let's start with a few points.
First, you can of course handle your own lifecycle change if you choose to NOT allow Android to reset your lifecycle on device rotate.
android:configChanges="orientation"
Use that flag in your manifest if you want to retain everything and handle your own rotation changes. However, if you have a layout-land folder with different XML files, you will not want to do this.
Yes of course you can do fragment.retainInstance when nested in Activities to get it to retain values. However, retaining populated Elements with values is not something you can just natively expect it to do as the UI elements were completely redrawn, so something has to tell it to redraw it again.
So if you are going old school and you are actually still doing findViewById and myText.setText('some Stuff'). Then you may find some time savings in using a library like icepick.
https://github.com/frankiesardo/icepick
However, the BEST option by a mile is to modernize your coding practice to use DataBinding. This allows you to not care about the UI interactions anymore as the values are bound to your Fragment or Activity or Model values by default and can be done with 2-way binding. This ensures databinding always populates with the value that was last updated.
Imagine you have an object of
public class Student implements BaseObservable{
String firstName;
}
Then in your xml you have
editText
android:text="#={student.firstName}"
Obviously there is a little more, like you need to set your student object in the onCreate to ensure it is in the XML for using. But when the user modifies the student firstname it is retained in the model, and redrawn automatically into the Edit Text.
This is your best solution, but depends on how invested you are in the future binding techniques of Android development or if you just prefer to go status quo to get across a finish line.
Hope that helps.
Add this in your Activity tag on your AndroidManifest.xml to avoid the recreation of the Activity and you won't lose the data.
android:configChanges="orientation|screenSize"

How to handle Screen rotation of activity which have more data?

I have one activity which has more than 20 fields, most of these fields will prompt user to set data.
Now the problem with the Configuration changes. If user set all the fields & he/she changed screen orientation, then all the fields will be reset because the activity will restart.
If I have only few fields means, then I would have gone with onSaveInstanceState() & onRestoreInstanceState().
But how to handle these many fields? Whether I have to go by storing all the fields? or Is there any better approach?
Try this way
<activity
android:name=".ActivityName"
android:configChanges="orientation|screenSize|keyboardHidden"/>
While you can prevent the activity from rotating to avoid the problem, this should only be done in special circumstances. Also, there are other situations where configuration changes can take place and cause problems. For a full discussion, see this: Why not use always android:configChanges="keyboardHidden|orientation"?
So my suggestion is that instead you create a holder class that implements Parcelable. Then on onSaveInstanceState(Bundle outState) simply call outState.putParcelable(key, yourParcelableObject). You would still need to update the corresponding fields on and this class but at least you would avoid having several keys and calling individual put to the bundle.
add android:configChanges="keyboardHidden|orientation|screenSize" to activity tag in manifest file but that is not best way please read Why not use always android:configChanges="keyboardHidden|orientation"? and http://developer.android.com/guide/topics/manifest/activity-element.html#config and http://developer.android.com/guide/topics/resources/runtime-changes.html for selecting best way for yourself here is a good example for using from parcle .

setRetainInstance not working for configuration changes in android

I have different design for potrait and landscape for activity.so i used layout-land layout .I put webview inside fragment and giving setRetainInstance(true) and loding url like google. when configuration changes it again loading can anybody tell how to avoid that .i didn't mention android:configchanges="orientation" in android manifestfile because
different design can anybody help in this question?
Thanks
Fragments can actually make this a lot easier. Just use the method Fragment.setRetainInstance(boolean) to have your fragment instance retained across configuration changes. Note that this is the recommended replacement for Activity.onRetainnonConfigurationInstance() in the docs.
If for some reason you really don't want to use a retained fragment, there are other approaches you can take. Note that each fragment has a unique identifier returned by Fragment.getId(). You can also find out if a fragment is being torn down for a config change through
Fragment.getActivity().isChangingConfigurations(). So, at the point where you would decide to stop your AsyncTask (in onStop() or onDestroy() most likely), you could for example check if the configuration is changing and if so stick it in a static SparseArray under the fragment's identifier, and then in your onCreate() or onStart() look to see if you have an AsyncTask in the sparse array available.

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.

Configuration changed (orientation change) and destroying Activities - is this the way it's supposed to work?

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)

Categories

Resources