I've been bothered by this "characteristics": When I use Back button to leave my app, I can tell onDestroy() is called, but the next time I run my app, all the static members of the Activity class still retain their values. See the code below:
public class HelloAndroid extends Activity {
private static int mValue; // a static member here
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText((mValue != 0) ?
("Left-over value = " + mValue) : "This is a new instance");
setContentView(tv);
}
public void onDestroy() {
super.onDestroy();
mValue++;
}
}
The above code displays the left-over value in mValue, and it increments when the session ends so that I know for sure the onDestroy() is called.
I found a useful answer on this forum, and I understand in the above code mValue is a class member, instead of an instance member. But isn't it true that, in this particular case, I have only one single HelloAndroid activity, and so When he dies, everything is cleaned up, and the next time I come back, everything starts over again? (Or, is there some other misterious thing in the system still holds on to it after onDestroy() so that it just won't die???)
(The above is just a variable, what if it's a bunch of objec references? Each piece is a separately re-collectable memory. Is there a chance that GC collects some of them but not all-or-none? This really bugs me.)
The OS decides when things "go away." The onDestroy is there to let your app have a final chance to clean things up before the activity does get destroyed but it does not mean that the activity will, in fact, be GCed. Here is a good article that I recommend people to read that relates to creating an exit button. While it's not exactly what you asked about, the concepts will help you understand what's going on.
You don't just have the Activity though. You also have the application, and its process running in a Dalvik VM. Android will usually leave the application running in the background until it needs to reclaim the memory it is using for some other application. Your static member should remain in memory as long as the process is running. If you try running some memory-intensive application or forcefully closing the running application with some task manager, you may see the static value reset.
Related
Android kills the application's process when it is in the background and it needs more memory.
I've read a few articles about this. Some people recommend restarting the app when this happens. But none of the articles give me information on how to do something like that.
Is there a way to go back to the root activity after an application's process has been destroyed and the app goes back into the foreground? What would be the best way to do something like this?
The only solution i found that works for me is putting this code in a base class for activities to inherit:
private static boolean isFirstOnCreate = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResource());
if (isFirstOnCreate && savedInstanceState != null) {
startActivity(getPackageManager().getLaunchIntentForPackage(getPackageName()));
finishAffinity();
}
isFirstOnCreateInvocation = false;
See my answer to this similar question or this answer to a similar question.
Basically you want to set a static variable when your app is started, and in each Activity you need to check if that variable is still set. If it isn't, it means that Android has killed the OS process hosting your app and created a new one after the user returns to your app. You can detect this situation and then do whatever is appropriate. Usually, this means redirecting the user to the first Activity and reinitializing your application.
I have an Android application with a single Activity (MainActivity). I also have a static state variable (foo) that needs to be started and stopped with MainActivity. The lifetime of foo must match the entire lifetime of MainActivity, not its visible lifetime, nor its foreground lifetime. Here's the basic gist:
static Foo foo;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
foo.start();
}
protected void onPause()
{
super.onPause();
if (isFinishing())
disposeFoo();
}
protected void onDestroy()
{
super.onDestroy();
disposeFoo();
}
private void disposeFoo()
{
if (foo.isRunning())
foo.stop();
}
Every now and again I will get a crash report that says: Foo has been started while already running.
I can start and stop MainActivity all day long from the app launcher and this crash won't occur. As far as I know, no one is calling startActivity on MainActivity either.
Is it expected behavior that a new instance of MainActivity will be created, and onCreate called on it before the onDestroy is run on the old instance all within the same application? In what circumstances would this occur? Is there a different pattern I should be using to initialize libraries, databases, and other singleton objects?
Your app is in very often killed if some other application with higher priority (generally, if it's in the foreground it's higher priority) needs the resources. This is due to the nature of mobile devices having relatively limited resources. You will find that it's static variables may be null once you return to it, so static variables for a longer periods of time in Android is a bad idea.
You should save your data somewhere more durable. You might find this article on general Data Storage to be useful. This question should be relevant too: Saving Android Activity state using Save Instance State
Because onDestroy is not called if, for example, activity was recreated because of screen rotation.
From the Activity documentation:
The final call you receive before your activity is destroyed. This can happen either because the activity is finishing (someone called finish() on it, or because the system is temporarily destroying this instance of the activity to save space. You can distinguish between these two scenarios with the isFinishing() method.
So you should call foo.start/foo.stop in onStart/onStop or onResume/onPause.
--
Update:
If I understand correctly, the problem is that you are tied to a singleton/monostate object Foo the should be unique for all objects, and must be destroyed when ALL activities are destroyed.
The problem is that nothing can guarantee that only one instance of activity has a runinng Foo, because onDestroy can be called after a new instance is created.
So the solution is to use an Instance counter:
public class Foo {
private int instCounter = 0;
public synchronized void start() {
...
++instCounter;
}
public synchronized void dispose() {
--instCounter;
if (instCounter == 0) {
// dispose
}
}
This should do the trick.
I have an activity in whose onCreate method an Init function is called (the function calls some native code involving lot of stuffs and calls to the openSLES audio api). The point is that this Init function makes the app crash when called again, which happens on a screen rotation or when i close the activity using Back button and i launch it again (but if in the meanwhile the process is killed, i have no troubles). I can't change the beaviour of the Init function.
I see that the process isn't killed when the activity is destroyed, I expected this after reading the docs, and it's a good thing since - if there is some audio signal playing - that continues playing after the activity has been destroyed, which is good for my purposes.
I tried to perform a check on the initialization state using onSaveInstanceState, but that works well only on screen-rotation, that's when onSaveInstanceState is called. The callback is not called when i push the Back button.
So i tried to use Shared Preferences, performing the state saving in onPause. But at this point i have the opposite problem: if the process is killed, the Shared Preferences values are kept, but in that case i need to perform Init again for the app to work properly.
I guess i need a way to know for sure if my activity is created after a process kill or not, but at the moment i can't see how. I thought about using the bundle instance in onPause method, but i can't figure how and whether this is possible. Any kind of hint would be really appreciated.
You can store pid of your process in shared preferences. If you compare in YourActivity.onCreate your current pid with stored one, you can determine when you must initialize OpenSLES.
You can initialize OpenSLES in Application-derived class, in YourApplication.onCreate - it's called only once.
edit:
I.e. declare following class:
public class YourApplication extends Application {
static private native synchronized void InitOpenSLES();
public YourApplication() {}
// see http://developer.android.com/reference/android/app/Application.html#onCreate() for details
#Override
public void onCreate() {
super.onCreate();
InitOpenSLES();
}
}
There's a simple solution to this problem. You don't need to save things in SharedPreferences to accomplish this. Just use a static (class) variable. Like this:
public class Globals {
public static boolean initialized = false;
}
The variable initialized will be set to false when the class is loaded. Only once. In your code, you then check and set the variable like this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initialize (but only once per process)
if (!Globals.initialized) {
init(); // Call init function that does things one time per process
Globals.initialized = true; // Remember we are initialized so we don't
// do it again
}
...
}
Even if all your activities are finished, if the OS doesn't kill your process the variable initialized will still be "true" if the application is started again. Once the OS kills the process, the variable will be set to "false" the next time the application is started and a new process is created.
For each and every process you have pid or process id. In your init function you can easily get the thread id and can save it in any integer value.
Thread.currentThread().getId()));
whenever your activity will restart just check that thread id is same or different. If thread id is different then call your function init function. Otherwise you have already done.
public class TestActivity extends Activity {
public static TestActivity mTestActivity;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mTestActivity = this;
}
#Override
protected void onDestroy() {
mTestActivity = null;
super.onDestroy();
}
}
Can I ask a very rookie question?
Does static link to Activity ALWAYS leads to memory leak? Even if I 'null' it on destroy?
EDIT:
If there is a memory leak even though you 'null' it on destroy, could you explain why?
I have some years of Java experience but I cannot understand why 'null' on destroy does not free the memory.
If, you null it on destroy there is not point in keeping it static. As for the leak, yes, I think you will (if you change activities). It would be easier to just keep a (non-static) reference to the application.
onDestroy runs once - if ever. Till then you have your leak. Unregister in onPause(). Notice that pre-Honeycomb onPause() might be the last method that would run - not that it makes a difference in your case as a process being killed takes its classes with it (and their leaks). Or so I think. I don't know if an activity can be killed without its onDestroy being called (not in the case the whole process goes down - then it is perfectly possible but it also makes no difference as I said) - I don't think so though.
In short your leak exists as long as your activity is alive (instantiated)
I am having SearchCritiera object and i make it singleton and declare this variable as static,
now problem is if i left my application remain open for couple of hours that static object is removed by Android OS, how can i make sure static object should not be removed by the OS.
like i know there are few keywords like
Weekreference and softreference is there any strongreference keyword which can tell Android OS do not remove the reference ??
Don't use static references, even if your application remains in the foreground, these objects may be destroyed by the garbage collector (I've seen this happening a couple times now).
You can simply avoid this by storing them in your unique Application instance. That object is guaranteed to live as long as your app.
Sadly, you can't force Android to keep your application in memory. If the OS feels it needs more memory for a foreground application or service it reserves the right to terminate one or all of your Activities.
I'm assuming what's happening is your static object is being lost and re-created when you call it, meaning that it has lost its state. That said, if you have a reference to the object in a foreground Activity I'm a little surprised that it's getting lost.
The best you can do work around this is hook into the lifecycle events and save the state of your singleton object and then restore it when appropriate.
Unfortunately, there are no Application wide lifecycle events in Androd. Have a look at this question for how to save transient application state.
If i am not wrong when application remain open for long time data is released by the android OS, and while stopiong activity it will call "onSaveInstanceState" and when can i store searchritiera into this method and will it retrive back when "onRestoreInstanceState" is get called ?
private static SearchCriteria searchCriteria;
#Override
public void onSaveInstanceState(Bundle outState)
{
outState.putSerializable(WLConstants.SEARCH_CRITERIA, searchCriteria);
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if(savedInstanceState != null){
searchCriteria = (SearchCriteria)
savedInstanceState.getSerializable(WLConstants.SEARCH_CRITERIA);
}
super.onRestoreInstanceState(savedInstanceState);
}