If I press home and come back to my app a little later I will find that the state has been preserved perfectly. For some reason however if I lock the phone and then unlock it, my app has been returned to the original state bar a few things here and there. When I looked into the logs I found that onCreate had been called while the phone was in a locked state. Because locking the phone is quite an off hand thing to do, having your game reset every time you do so is not desirable to the user. How can this be avoided at least for a longer period of time than a few seconds after locking the phone?
This is how Android OS works, it decides by it's own when to destroy your view. To avoid loosing this information there is a Method that can be reimplemented in your activity
#Override
public void onSaveInstanceState(Bundle outState){
iGameContent.saveGame(outState);
}
Save all your needed data into outState, and in the onCreate method, check if its a new instance or saved instance, like this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game);
if (savedInstanceState!=null){
iGameContent.loadGame(savedInstanceState);
}else{
// Normal initialization
}
}
An example of the save/load to a Bundle is the following:
public void loadGame(Bundle aBundle){
iBadsHit = aBundle.getInt("iBadsHits",0);
}
public void saveGame(Bundle aBundle){
aBundle.putInt("iBadsHit", iBadsHit);
}
If your log is showing that onCreate has been called then that means your apps process was killed.
Do you know the Android Activity Lifecycle? If not, read up on it here: Android Activities
The behavior on screen lock could vary from one device to other. Some events could cause the destruction of the app. You can try to handle some of this events to avoid this situation specifying it on the AndroidManifest.xml:
android:configChanges="keyboardHidden|orientation"
These two are the most problematic in screen lock. Yo can find more information on the last chapter of this nvidia document
Related
Can somebody clarify please why I have such a weird behavior. Up to documentation the Bundle savedInstanceState which is set in onSaveInstanceState() is alive as long as application alive, so when it it in foreground or background. After the application is being killed the savedInstanceState instance is killed as well. Here is what I have:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (savedInstanceState != null) {
Log.i("Dev", "not null");
} else {
Log.i("Dev", "null");
}
}
Here is how I set it:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("bool", true);
}
Then, I'm starting the application in the emulator. After application is opened I click home button so the Launcher is visible. Then I kill the application's process using adb. After that I start the application from the list of recently used application expecting for "null" in the Logcat, but what I actually see is "not null", so my understanding is incorrect?
The Bundle is saved for as long as Android wants it to be saved/can save it. One of the "features" (quotes because it ends up being a bad idea as often as it is a good one) of Android is that applications are never exited (to the user's view). Their mechanism of doing this is the onSaveInstanceState- it stores the Bundle, and when the app is later reinitialized by some method (such as from the recent activities menu) it will pass that Bundle to the onCreate and let it re-initialize itself.
Of course this also causes problems. For example, if you save login info, exiting an application won't log you out. So a user can then just hand his phone to a friend to watch a video, thinking that he exited his mobile banking app and is safe, yet the friend can call it back up and recreate it. If your app has large data structures in static variables or singletons they will not be recreated unless you code it carefully. Apps that require activities to be explored in order can be restarted from the middle.
Now Android can choose to forget your Bundle. If you put several MB in it, I would expect android to forget it rapidly. But it will remember it for as long as it can.
Isn't it very clearly stated here ? Or do I missunderstand your question?
[..]To save additional data about the activity state, you must override the onSaveInstanceState() callback method. The system calls this method when the user is leaving your activity and passes it the Bundle object that will be saved in the event that your activity is destroyed unexpectedly. If the system must recreate the activity instance later, it passes the same Bundle object to both the onRestoreInstanceState() and onCreate() methods.
I mean for me this is also reasonable in most situations. Because when your activity / app is in the background and the android system closes it (let's say because it needs more memory), then it first saves the state. So next time the users opens your activity, you can restore it's previous state (and that may also exactly be what the user wants, since it wasn't him who closed the activity, but the system itself).
I am new to Android development and I am still not able to understand the onPause() and onStop() methods in an activity.
In my app, I have a static class that I name Counter, and it keeps the state of variables in memory for the app. My app runs fine in the emulator. What I was trying to test was differential behavior of onPause() versus onStop().
For onPause, I wanted the values stored in the Counter class's members retained, whereas calling onStop() I wanted the counter values reset to zero. So I override onStop() and set the variables inside the counter class to zero. However, in the emulator, I cannot seem to get the app in the Paused state. In the emulator, I open my app, exercise it. Then I hit the home button (not the back button) of the emulator, and launch another app, believing that this would mimic onPause() activity. However, the emulator does not appear to honor this (I am using an armeabi v7a emulator), it seems to always be calling onStop() because my counter values all go back to zero, per my override in onStop(). Is this inherent to the emulator or am I doing something wrong to get my activity into the paused state?
I'm not sure which emulator you are testing with, but onPause is the one method that is always guaranteed to be called when your Activity loses focus (and I say always because on some devices, specifically those running Android 3.2+, onStop is not always guaranteed to be called before the Activity is destroyed).
A nice way to understand the Activity lifecycle for beginners is to litter your overriden methods with Logs. For example:
public class SampleActivity extends Activity {
/**
* A string constant to use in calls to the "log" methods. Its
* value is often given by the name of the class, as this will
* allow you to easily determine where log methods are coming
* from when you analyze your logcat output.
*/
private static final String TAG = "SampleActivity";
/**
* Toggle this boolean constant's value to turn on/off logging
* within the class.
*/
private static final boolean VERBOSE = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (VERBOSE) Log.v(TAG, "+++ ON CREATE +++");
}
#Override
public void onStart() {
super.onStart();
if (VERBOSE) Log.v(TAG, "++ ON START ++");
}
#Override
public void onResume() {
super.onResume();
if (VERBOSE) Log.v(TAG, "+ ON RESUME +");
}
#Override
public void onPause() {
super.onPause();
if (VERBOSE) Log.v(TAG, "- ON PAUSE -");
}
#Override
public void onStop() {
super.onStop();
if (VERBOSE) Log.v(TAG, "-- ON STOP --");
}
#Override
public void onDestroy() {
super.onDestroy();
if (VERBOSE) Log.v(TAG, "- ON DESTROY -");
}
}
I know your question was 6 months ago but in case someone else stumbles on this question:
am I doing something wrong to get my activity into the paused state.
Yes, you are. This:
I hit the home button (not the back button) of the emulator, and
launch another app, believing that this would mimic onPause()
activity.
Hitting the home button will indeed call the onPause() method but because the home button makes your activity no longer visible it will then call the onStop() method (like patriot & milter mentioned).
As per the Activities developer reference (http://developer.android.com/guide/components/activities.html) you can display a dialog or simply put the device to sleep.
Alternatively, you call an activity that will only partially obstruct the calling activity.
So call an activity that creates a window with a view of size:
android:layout_width="100dp"
android:layout_height="100dp"
Which doesn't cover the entire screen, thus leaving the calling activity behind partially visible, thus calling only calling activity's onPause() method.
Clone that activity so that both view sizes are "match_parent" instead of "100dp" and call it and both the onPause() and onStop() methods of the calling activity will be called because the calling activity won't be visible.
There can be exceptions of course, like if the called activity causes an app crash in either of its onCreate(), onStart() or onResume() then the onStop() of the calling activity will not be called, obviously, I'm just talking about the general case here.
The differences between when onPause() and onStop() are called can be pretty subtle. However, as explained here, onPause() will usually get executed when another activity takes focus (maybe as a pop up, or transparent window) while the current activity is still running. If you navigate away from the app completely (for example, by hitting the home button), the activity is no longer visible and the system may execute onStop(). I only say may because, as Alex mentioned, there are some cases where onStop doesn't get called before the Activity is destroyed.
onPause():
"If an activity has lost focus but is still visible (that is, a new non-full-sized or transparent activity has focus on top of your activity), it is paused. A paused activity is completely alive (it maintains all state and member information and remains attached to the window manager), but can be killed by the system in extreme low memory situations."
onStop():
"If an activity is completely obscured by another activity, it is stopped. It still retains all state and member information, however, it is no longer visible to the user so its window is hidden and it will often be killed by the system when memory is needed elsewhere."
Taken from android reference activity class: http://developer.android.com/reference/android/app/Activity.html
If you are emulating Android 4.x you can control how the system handles background activities using Settings -> Developer Options -> Don't keep activities and Background process limit. For older versions there is an app called Dev Tools which contains the same settings. However, on low memory conditions the system can disregard those settings and terminate your application. Increasing the amount of memory assigned to the emulator might help.
Also, if you are re-launching your app from Eclipse, it will kill the previous process instead of gracefully terminating it.
I agree with milter!
onPause():
"If an activity has lost focus but is still visible (that is, a new non-full-sized or transparent activity has focus on top of your activity), it is paused. A paused activity is completely alive (it maintains all state and member information and remains attached to the window manager), but can be killed by the system in extreme low memory situations."
If you swap applications without pressing Back (press and hold HOME) then the OS is going to call onPause. When you return to your activity (press and hold HOME again) in onResume you should have all of your private variables preserved. But you can't control the user, right?!
if you anticipate that the user is going to leave your app and the OS calls your onStop you better save your data if you intend to resume where you left-off.
I have a Timer also, I need to save the elapsed time so when the user returns I can restore the data.
here is my example to save:
#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.
savedInstanceState.putLong("elapsedTime", elapsedTime);
// etc.
}
And my code to restore:
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
elapsedTime = savedInstanceState.getLong("elapsedTime");
}
Place these methods inside of your class and you are good to go. Keep in mind that the string "elapsedTime" in my case is a KEY to the system and it must be unique. Use unique strings for each piece of data that you would like to save. For example "startClock", "ClockTextColor", etc...
I have an activity which sometimes gets destroyed by the system as a response to PowerManager.goToSleep(...) being called. The behavior is not consistent and I can't figure out the reason for this.
Here is the relevant code for my activity's onCreate which is the most relevant part of code:
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
IntentFilter userprsentfilter = new IntentFilter(Intent.ACTION_USER_PRESENT);
mUserPresentReceiver = new UserPresentReceiver();
registerReceiver(mUserPresentReceiver,userprsentfilter);
Log.d(TAG, "created.");
}
protected void onDestroy() {
Log.d(TAG, "finished.");
}
Somewhere, on some distant service and sometimes by the activity itself,
PowerManager.goToSleep(SystemClock.uptimeMillis())
is called causing the activity the get destroyed.
Can anyone please shed some light on why the system will try to sometimes destroy an activity when PowerManager.goToSleep is called? Also, is there a way to make the sure or lower the chances of the activity getting destroyed by the system? I can say with certainty that resources are not scarce when this happens.
All Android apps are subject to being shut down at any time. This can happen when the app is backgrounded, when it is consuming too many system resources, or when the phone sleeps. You can't (and shouldn't try to) alter that behavior: it does this on purpose and for very good reasons.
If you have an operation that needs to continue running under these circumstances, you need to implement it as a Service. This will allow it to run even when the phone is sleeping.
I have Activity, which is to save its data in case system decides to kill it while it is in the background.
So, I’ve got onSaveInstanceState:
#Override
protected void onSaveInstanceState(Bundle outState){
outState.putString("value", "some_value");
}
I check whether Bundle object is null in onCreate:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isRestarted=(savedInstanceState==null);
How do I write test method? I tried
public void testRecreate(){
Instrumentation mInstr=this.getInstrumentation();
mInstr.callActivityOnSaveInstanceState(mActivity, null);
mActivity.finish();
mActivity=this.getActivity();
assertEquals(false, mActivity.isRestarted);
}
but it seems to be wrong.
You can use some hidden API functionality. In your test's setup, call the
android.app.ActivityManagerNative.getDefault().setAlwaysFinish()
method via reflection (because it's a hidden API) and confirm that the value was successfully set using
android.provider.Settings.System.getInt(getContentResolver(), Settings.System.ALWAYS_FINISH_ACTIVITIES, 0)
Then in the test cleanup, set this setting back to false.
Enabling the AlwaysFinish setting causes the system to destroy activities as soon as they are no longer on the screen, immediately triggering the onSaveInstanceState event. For this code to work, you will need the ALWAYS_FINISH and WRITE_SETTINGS permissions.
See the code for the SetAlwaysFinish tool linked in this blog: How to test onSaveInstanceState and onRestoreInstanceState on a real device
For manual testing, this routine works:
Launch the activity you want to test
Hit the home menu button on your device (activity is stopped)
Kill the process from DDMS (activity is still not destroyed)
Bring back your app again from the recent apps list
This should trigger onCreate again on your activity while not restarting the entire application. If you're not saving and loading the state properly, you'll know soon enough.
If you haven't been through it before, I found it worthwhile to do this at least once.
Stuff like that could be easily test using robolectric. Check it out!
I have an app that starts playing sounds and begins/resumes gameplay in the onResume() method, but what I'm noticing is that if my app was the last run application when I put the phone into standby (screen off), and I just press the Menu button to check the time, then the phone starts playing the game and sounds in the background (the app isn't actually visible, only the screen with the date/time is, yet onResume must have been called in my app). What am I to do here? Is there a way to discern what is reactivating the app, and then add a conditional statement that only starts the game when the app is actually visible?
Here is a snippet from my onResume:
#Override
protected void onResume()
{
mySaveGame = Utilities.loadSavegame(this);
//check the savegame
if(mySaveGame!=null)
{
//start game using savegame values
this.startFromSavedGame(mySaveGame.getIsLevelComplete());
}
else
{
//run the 1st-run components
this.startFirstRun();
}
super.onResume();
}
The only thing I can think of doing to prevent the game from starting whenever the screen gets turned on (even when the app isn't visible) is to put this.finish() as the last line in onPause()... but that forces you to restart the app every time you want to go back to it because the precess itself was killed (which is fine because my onPause saves persistent data, but it's not an elegant solution).
Please help.
Have you considered switching to onStart() and onStop(), rather than onResume() and onPause()?
I was having the same problem (I had my music player resume/pause at onResume()/onPause()) and the best solution I found is to pause and resume my activity when it is on the foreground which you can get with public void onWindowFocusChanged (boolean hasFocus) callback.
Edit: This in an old and slightly incorrect answer - correct response is described in the Android Developers Blog: making android games that play nice