In the application I'm building, I need to detect the application quitting if and only if the application has been quit when its in the background because the OS is reclaiming memory.
From my own experimentation, the onDestroy is called on EVERY instance. I've tried checking for isFinishing but I'm not 100% sure which situations this isolates it to.
#Override
public void onDestroy()
{
super.onDestroy();
Log.i("V LIFECYCLE", "onDestroy");
if (!isFinishing())
{
// are we here because the OS shut it down because of low memory?
ApplicationPreferences pref = new ApplicationPreferences(this);
// set persistant flag so we know next time that the user
// initiated the kill either by a direct kill or device restart.
pref.setThePersistantFlag(true);
Log.i("DEBUG", "onDestroy - ensuring that the next launch will result in a log out..");
}
}
Can anyone shed light on my issue here? Thankyou.
Through trial and error I have worked out a solution that works perfectly for anyone thats interested. I have narrowed down the case when the application state is being resumed (onResume) in the case of the OS reclaiming memory.
public boolean wasJustCollectedByTheOS = false;
#Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
super.onSaveInstanceState(savedInstanceState);
// this flag will only be present as long as the task isn't physically killed
// and/or the phone is not restarted.
savedInstanceState.putLong("semiPersistantFlag", 2L);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
long semiPersistantFlag = savedInstanceState.getLong("semiPersistantFlag");
if (semiPersistantFlag == 2L)
{
savedInstanceState.putLong("semiPersistantFlag", 0L);
this.wasJustCollectedByTheOS = true;
}
}
// this gets called immediately after onRestoreInstanceState
#Override
public void onResume() {
if (this.wasJustCollectedByTheOS){
this.wasJustCollectedByTheOS = false;
// here is the case when the resume is after an OS memory collection
}
}
I don't know whether its help you or not,
From Android Activity class,
public void onLowMemory ()
This is called when the overall system is running low on memory, and would like actively running process to try to tighten their belt. While the exact point at which this will be called is not defined, generally it will happen around the time all background process have been killed, that is before reaching the point of killing processes hosting service and foreground UI that we would like to avoid killing.
Applications that want to be nice can implement this method to release any caches or other unnecessary resources they may be holding on to. The system will perform a gc for you after returning from this method.
And Since: API Level 14
public abstract void onTrimMemory (int level)
Called when the operating system has determined that it is a good time for a process to trim unneeded memory from its process. This will happen for example when it goes in the background and there is not enough memory to keep as many background processes running as desired. You should never compare to exact values of the level, since new intermediate values may be added -- you will typically want to compare if the value is greater or equal to a level you are interested in.
Related
I am doing a lot of reading on this currently but thought it never hurts to ask a knowledgeable group of developers as well.
This is a popular issue and there seem to be a lot of different ways of handling it.
I have an Android application. It uses OpenGL so when it starts up does a fair amount of data loading both from disk and then in VRAM. When the screen is rotated as well as when the power button is pressed (to put the phone into sleep mode) I lose all data and the activity is killed.
I don't want this. The app takes about 7 seconds to boot up and load its data and it is really annoying if you just sleep your phone for a second then have to wait the app to load all again.
I am familiar with the app lifecycle as outlined by Google but having a hard time implementing a solution that works for my particular case.
Any suggestions? My overall goal is to preserve the following in memory when the phone sleeps are is rotated:
1. App memory on heap
2. App memory in VRAM
Thanks!
So far:
public class MainActivity
private FH_SurfaceView fGLSurfaceView;
#Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
}
#Override
public void onRestoreInstanceState(Bundle savedState)
{
super.onRestoreInstanceState(savedState);
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Check if the system supports OpenGL ES 2.0.
final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000;
if (savedInstanceState != null) {
/* NEED TO DO SOMETHING HERE?? */
return;
}
else{
this.fGLSurfaceView = new FH_SurfaceView(this);
setContentView(fGLSurfaceView);
}
}
#Override
protected void onResume()
{
super.onResume();
}
#Override
protected void onPause()
{
super.onPause();
}
}
The problem is that your Activity is holding a large amount of data. When the phone sleeps, it will likely target it to be destroyed for a variety of reasons, primarily to make sure there's memory for other tasks, but also because it might eating up processing cycles and Android knows it is not visible. Hence your problem. This does not happen to all apps, only "heavy" ones.
There may be nothing that you can do about this, but here are some suggestions:
Analyze your data loading. It might be that some parts are computationally expensive and therefore they are slowing the process, while other parts are heavy on memory but light on processing. Then in onPause dump the heavy memory part to improve the odds that Android won't kill your app. You might be able to get a 1-2 second load time instead of your current 7.
Consider creating a Service where the data resides and then binding to it in order to access it. Android does not expect a Service to be visible, and you can signal processing to cease in the onPause or onStop of your Activity to prevent Android from killing the service. I realize this is "tricky" as your data is currently in a custom SurfaceView class - and storing display data not on the UI thread is a pain.
Also, you might put the data into the Application class for similar reasons. The benefit of this is that you do not need binders to access the data. The drawback is that Android is more likely to destroy this than a Service I think (but that's experience talking, not testing - so, educated guess?). Also, messing with the Application class should be done with caution.
If all else fails, then try to provide an entertaining transition that acknowledges the silliness of the situation. In other words, communicating that you are aware of an unpleasant situation to a user is far better than ignoring it and hoping they don't mind.
In android,is there any callback that is called when system runs low on memory or out of memory occurs.
For eg: there are three applications A,B,C running.In C out of memory is thrown.How can the application A come to know that so that it can clear its resources ,so that the android system wont kill the application A.
You must extend the Application class, and override onLowMemory() method.
public class App extends Application {
#Override
public void onLowMemory() {
super.onLowMemory();
// handle lowmemory stuff
}
}
Besides onLowMemory() there are other methods.
Use WeakReference to keep your cache and Android will handle OOM gracefully. Also, you can use a Service, which has higher priority than activities and will be destroyed after them.
override onLow memory method and do the stuff that you needed.
#Override
public void onLowMemory() {
// clear the unwanted resources from memory
super.onLowMemory();
}
NOTE: There is no guarantee that this method will be invoked correctly.
This is called when the overall system is running low on memory, and would like actively running process to try to tighten their belt. While the exact point at which this will be called is not defined, generally it will happen around the time all background process have been killed, that is before reaching the point of killing processes hosting service and foreground UI that we would like to avoid killing.
From developer Docs Link
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.
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