I'm pretty new to android development and I need some help saving the state of an activity.
What is the correct way to save the instance from onPause and restoring it from onRestore since obviously Android isn't sending the savedInstanceState Bundle as it does with onCreate or onSaveInstanceState for example. Or is there a better way to save other than using the savedInstanceState bundle?
Does this make sense?
[edit]
Ok, i think i know what my real problem is... But first, I think what I was looking for was to use SharedPreferences instead of savedInstanceState.
So, doing more debug log watching I'm noticing that instead of bringing the Activity to the top of the stack it's creating a new one. Yes, I realize I'm creating a new one....
Intent itemintent = new Intent(MediaList.this, AudioPlayer.class);
Bundle b = new Bundle();
//...putString some strings to send
itemintent.putExtra("android.intent.extra.INTENT", b);
itemintent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivityForResult(itemintent,0);
...But isn't FLAG_ACTIVITY_REORDER_TO_FRONT supposed to stop it from creating a new activity? I'm guessing it thinks it has to create a new one since i'm sending along some strings?
Better yet, how can I check if the activity is already in the stack and switch to it as long as the strings are the same? -- I'm starting this activity when the user clicks a media item from a listview.
[/edit]
For some reason this is not documented in very very bold neon flashing letters, and took me a while to discover, but you don't need to worry about whether your activity exists or not if you simply create it with the
android:launchMode=["multiple" | "singleTop" |
"singleTask" | "singleInstance"]
property set to "singleInstance".
Then there is only ever one instance of it, and your memory fields are preserved as long as the activity hasn't been garbage collected.
The other thing you can do is to create an Application (extended from Application) and store all your persistent objects up in that... it is created first and destroyed last as far as your entire app's life cycle is concerned (including all your activity-less services).
Just create an Application class and specify it in your manifest like this:
<application android:icon="#drawable/icon" android:label="#string/app_name"
android:name=".MyApplication">
Then if you still REALLY want to save your values for situations where the app is going to be closed, just use SharedPreferences.
What is this onRestore method you speak of? It is not part of the Activity lifecycle... I will suppose you mean onRestart. Anyway, the reason you don't get a bundle for onRestart is because you don't need one. Your activity hasn't been officially "killed" so you don't need to restore from a saved state. Your activity was paused, but not removed from memory, so the system is just telling you that it was made visible again. You probably don't need to do anything for this sort of transition event.
Other than that, the way to do it is to save anything you consider a "state" value in the onSaveInstanceState(), then restore them in onCreate. After that you can restore any view-specific properties either on the onCreate itself, or later in the Activity lifecycle (e.g. onResume).
Related
How can I save activity state (B) when moving to previous activity (A) and how to restore it's previous state when moving back (to activity B)?
SCENARIO
Content of activity B can be changed by user. After finishing work, performed changes can be persisted (e. g. saved to local DB). Before persisting, user may want to navigate back to previous activity. At this point, "work session" should be somehow saved temporary in memory. After returning back to activity B "work session" should be restored, so user can continue in work.
APPROACHES
Standard methods onCreate(Bundle) and onSaveInstanceState(Bundle) are used to restore/save activity state when device configuration changes (e. g. on rotate). As onSaveInstanceState(Bundle) is not part of activity's life-cycle, this method does not get called on "back-press" when activity is destroyed (by default finish() is called). The same applies for onRestoreInstanceState(Bundle). Obviously these methods alone are not sufficient to restore activity state after lefting an activity.
Transfering bundle state between activities
One way I can think of is to override onBackPressed() in such way it will start an Intent to previous activity A with bundle state Intent#putExtras(Bundle) of current activity B. When moving back, starting an Intent to activity B, this bundle state will be delivered back Intent#putExtras(Bundle) to restore the state of activity B. However this also require to override onCreate(Bundle) and onSaveInstanceState(Bundle) in A so bundle state of B would not be lost on configuration change before navigating back to B.
Not finishing an activity on back-press + non-default launchMode
Another way (more cleaner) is to override onBackPressed() so that it would start Intent to previous activity A without calling finish() of current activity B so activity B will hang in memory in paused state (waiting for resume). To resume activity B, manifest configuration android:launchMode="singleInstance" is required so android will use existing activity (paused B) instead of creating new one (B2) when navigating back to activity B (startIntent(B.class)).
Details: launchMode singleInstance creates singleton activity in new task (task = set of activities with the same group id i. e. affinity, normally app activities have the same affinity i. e. app = single task).
Drawback: transition animations does not work with singleInstance mode. It seems that singleInstance task may not be fully initialized at animation time. For more details: Custom animation doesnt work on SingleInstance Activity.
Saving activity state to SharedPreferences
Saving activity state to SharedPreferences on "back-press", restoring activity state from preferences in onCreate(Bundle) like in following link Save activity state to SharedPreferences.
Drawback: unable to save Bundle state (only primitives: putInt, putString, ...).
Others
Some of the methods listed in Share data between activities can be used. Answers from this link refers to developer.android.com/guide/appendix/faq/framework.html which is unfortunately broken. Here is an alternative source http://wing-linux.sourceforge.net/guide/appendix/faq/framework.html.
About sharing data via Application object:
Don't Store Data in the Application Object,
Using the Android Application class to temporary persist data.
Based on Application class documentation, using static singletons over Application is more preferable.
Base class for maintaining global application state. ...
Note: There is normally no need to subclass Application. In most situations,
static singletons can provide the same functionality in a more modular
way. If your singleton needs a global context (for example to register
broadcast receivers), include Context.getApplicationContext() as a
Context argument when invoking your singleton's getInstance() method.
It seems that putting data in Application object or static singleton is so far the best solution for this problem.
QUESTION
Is there any build-in solution for this (without need of overriding onBackPressed())? For example saving activity on back-stack with it's state. If not, what is the common pattern to save activity state in such situation?
RELATED POSTS (just to link them with this post)
How do I save temporary data on android?
For an alternative way to handle this, you can map your view's states to a data structure and save it as an object notation (such as json) to your local and when your activity is created / re-created you can read your state from local and bind them with your views.
For more information and example, you can check Spotify's presentations and open source project which contains information about how they manage application's ui from apis. (I know it's not exactly what you're trying to do but you may find some tricks.)
If you need to save your data in your activities, you can use Local Db or in-memory cache.
When you back out of an activity, the activity is not just destroyed programmatically, but also conceptually. In other words, the user expects it to be gone. We talk about saving activity state in situations where the activity object is destroyed but the user perceives that it still exists, such as during configuration changes or when it's in the background or backstack.
What you're trying to save is properly thought of as application state, not activity state. As such, SharedPreferences is an appropriate place for it. Whether it's the best solution is a matter of opinion and depends on your use case.
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.
Very new to Android but when I am, say, in activity A, and I intend to move to activity B, then go back to activity A, all the data displayed in activity A before going to to B is lost. I never call finish(), am I doing something wrong?
I recommend going through Google's training and learning about activity life cycles.
http://developer.android.com/training/basics/firstapp/index.html
With your two activities, override all of the life cycle methods (onCreate, onResume, etc) and put a simple log message in them. Just move between the activities, and watch your log to learn the order of events.
From there, learn about Bundles and how to save your instance states and you'll be well on your way to making apps!
Check that all your Views do have identifiers in your XML. Android restores only those views which have identifiers.
Additionally, for your custom data (not views):
It can be that your Activity A got destroyed while in background.
Normally they do like that:
1) Add onSaveInstanceState() to your Activity. There put all your custom data in the Bundle argument. Be sure call super at the end. You do not need to store your views, just your custom data such as your custom variables etc.
2) in onCreate see if the savedInstanceState Bundle argument is null.
3) If null populate with your default data.
4) If not null restore the data from the Bundle and populate with them
The Android Developer Guide states that activities are launched via Intents:
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
For Fragments, the usual way to display it on the screen is as follows:
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
Why is it that in one case I have to specify a class, and in the other an object? I.e., I would like to use something like
Activity nextActivity = new SignInActivity();
Intent intent = new Intent(this, nextActivity);
startActivity(intent);
Because Activity lifecycle is managed by Android whereas Fragment lifecycle is tied to the Activity in which it is contained.
As mentioned, Activity lifecycle is managed by Android. This is required, among other things, for Android to manage the system resources and also to take care of the back stack.
Fragment, on the other hand, was introduced to modularize and better organize the UI for devices with different sizes. According the the documentation:
Starting with HONEYCOMB, Activity implementations can make use of the
Fragment class to better modularize their code, build more
sophisticated user interfaces for larger screens, and help scale their
application between small and large screens.
To answer the latter part of your question, you can indeed pass the results of an activity to a second activity. But you should never create an instance of an Activity class for that. The right way is to use the startActivityForResult() and send the resulting value to the destination activity through the Intent.
While adding fragment, you are already specifying where exactly to insert that fragment into. So, the ideal way is to,
Create your fragment.
Insert into a layout of your current activity.
Use transactions to remove/manage your fragments, added to the current activity.
In no way, you could launch or use just a fragment, without attaching it to an existing activity.
Android handles Activity life cycle by itself. Just look at the methods of Activity class, they're just like a fill in the blanks. Android calls the shots here. Through these methods it just ask if you want to do something when this activity is created, resumed, paused etc.
The reasons for Android handling activity life cycle internally, are many:
Properly setting up an Activity involves lots of boiler plate code, better let system do it for you. The whole Context and window management is set up for you behind the scenes. Imagine the amount of extra work, if you had to do it for every Activity you created.
Activities are shared, home screen and other applications might want to launch/use them. How would be this possible if they have to call new MyActivity() of some obscure package ? . This is why Activities and other externally invokable components must be declared in application manifest.
Activities from many applications can be parts of an android task ( a piece of work from user's perspective). And are automatically placed/removed/re-arranged on a back-stack. Again, its better Android manage their creation and destruction rather than developers messing with this whole setup.
All user cares is that an Activity must show up when asked for, and just get out of the way if user navigates somewhere else. Android enforces this. Making an Activity appear on its own, or refuse to go away, just because its allowed to be programmed that way, is unacceptable.
Now Fragments , on the other hand are internal. They live inside an Activity and are not accessed from or shared with outside applications or tasks in any way. Fragments are even not a part of application manifest and hence are not exposed outside. Android need not worry about each fragment separately, because fragment life-cycle is bound to that of its parent Activity. Android doesn't care what you do with fragments internally, it can just end the activity and everything inside it is destroyed as well.
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.