10

I have an android app that is setup to start a Java activity (call it MyJavaActivity), which in turn launches a NativeActivity. When the NativeActivity finishes it returns back to MyJavaActivity.

I also have a Java singleton class (call it MyJavaSingleton) which I would like to stay in memory throughout my application's lifecycle. I set some of the singleton class's member variables from my NativeActivity (using JNI), which can later be retrieved by MyJavaActivity.

The problem is, MyJavaSingleton instance seems to be in memory until NativeActive exits, but somehow seems to be set to null again when MyJavaActivity starts up again, so all the variables I had set in NativeActivity are now reset to their defaults. Why does this happen?

 public class MyJavaActivity extends Activity implements View.OnTouchListener
 {
   @Override
   public void onCreate(Bundle savedInstanceState) 
   {
    super.onCreate(savedInstanceState);
    MyJavaSingleton.Instance().DoStuff();
   }

   @Override
   public boolean onTouch(View arg0, MotionEvent arg1) 
   {
      Intent intent = new Intent(MyJavaActivity.this, NativeActivity.class);
      startActivity(intent); // at this point, android_main will be executed 
   }
 }

 /////////////////////////////
 public class MyJavaSingleton
 {
   static private MyJavaSingleton mInstance = null;
    synchronized public static MyJavaSingleton Instance()
{
    if( mInstance == null )
    {
        mInstance = new MyJavaSingleton();
        Log.v(TAG, "New MyJavaSIngleton instance");
    }
    return mInstance;
}
 }
 /////////////////////////////
 // Native 
 void android_main(struct android_app* state) 
 {
     // Do various stuff, set some variables on the MyJavaSingleton
     // At this point, MyJavaSingleton.mInstance is still at the same address in memory, which is good //
     ANativeActivity_finish(state->activity);
     exit(0);
     // Problem: sometime after this exit and before MyJavaActivity::onCreate is called, MyJavaSingleton.mInstance is set to null! 
 }

In the above code extraction, "New MyJavaSIngleton instance" is printed when the app is first started, and then again right after the NativeActivity exits (ie after android_main is exited) and MyJavaActivity's onCreate is called again.

Why does MyJavaSingleton.mInstance become NULL when reentering MyJavaActivity?

lost_bits1110
  • 2,380
  • 6
  • 33
  • 44
  • 2
    why are you calling exit()? Doesn't that kill the JVM, or am I being ignorant of Android? – duffymo Sep 21 '11 at 17:30
  • Yes, I don't like having to call exit() either, I feel this is a hack, but the application does not seem to return to MyJavaActivity unless I do this. – lost_bits1110 Sep 21 '11 at 17:45
  • @lost_bits1110 `finish()` doesn't work? – DeeV Sep 21 '11 at 17:46
  • As @James said you singleton should be private. Also, as written, `Instance()` should be `synchronized`. – Miserable Variable Sep 21 '11 at 17:48
  • @DeeV No, In my code snippet above you can see I call the native equivalent to 'finish()`, which is `ANativeActivity_finish`. With only this (and no `exit()`), the app does not return to `MyJavaActivity`, it simply stops at the last screen that `NativeActivity` was showing. @Hemal I mentioned this in a previous comment - but I had originally set it to `private` and the behavior is the same. I have tried adding `synchronized' though still the same results :( I will update my code above to show these two changes. – lost_bits1110 Sep 21 '11 at 17:54
  • I don't know how class loading works in Android but there would be one instance of the `static` for each classloader that loads the class, because there would be one instance of class per classloader. You could add a a `static {System.out.println("Loading MyJavaSingleton");}` if this could be the case – Miserable Variable Sep 21 '11 at 17:59
  • @Hemal - Thanks for the tip! I added this static print statement and it gets called twice, each time right after `MyJavaActivity::onCreate' is entered. Not sure I understand why though? – lost_bits1110 Sep 21 '11 at 18:12
  • I guess because the class got unloaded earlier. You could print the classloader in the static block to see if they are different. `MyJavaSingleton.class.getClassLoader()`. I don't know enough about Android so I am wondering why `MyJavaActivity.onCreate` even gets called again. Is the application starting again? – Miserable Variable Sep 21 '11 at 18:28

4 Answers4

11

Each Android app runs in its own process so as long as it keeps running, your singleton will be available. When the process dies, your singleton is lost. Therefore when the app is re-launched this singleton object will need to be re-created.

RHT
  • 4,974
  • 3
  • 26
  • 32
  • Yes but I never quit the app entirely, the app is simply going from MyJavaActivity to NativeActivity and then back to MyJavaActivity. – lost_bits1110 Sep 21 '11 at 18:03
  • I think calling the `exit` must be killing the process and then it restarts somehow bringing me back to `MyJavaActivity`. Unfortunately using `exit` seems to be the only way to resume back to the Java activity that called it :( – lost_bits1110 Sep 22 '11 at 18:05
2

Be sure to read the documentation on the process lifecycle:

http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html#Lifecycle

In this case, whenever your app is in the background (in terms of activities, none of your activities are visible to the user), your process can be killed at any time. If the user later returns to one of your activities, a new process will be created and a new instance of the activity created.

hackbod
  • 90,665
  • 16
  • 140
  • 154
  • 1
    This is a good comment, however this page says 'By default, all components of the same application run in the same process'. I do not change this default mechanism, so I am assuming that `NativeActivity`, `MyJavaActivity`, and `MyJavaSingleton` are all part of the same process. Either `MyJavaActivity` and `NativeActivity` are always visible at some point (except when transitioning between them), so I don't know why my app's process would ever be killed or why my original singleton instance is lost and a new singleton instance is created. – lost_bits1110 Sep 21 '11 at 18:35
  • 2
    If the singleton is becoming null, then either you are setting it null somewhere, or your process is going away. If you are actually never going out of the foreground, the only other way it could go away is if it crashes or uses so much memory it had to be killed. – hackbod Sep 24 '11 at 00:34
1

I'd make mInstance private. Just in case some other code is accidentally setting it.

James DW
  • 1,815
  • 16
  • 21
  • I'm not sure who downvoted, but I had initially set it to private, and ended up setting it to public so that I could quickly print out its address values. Setting to private still causes my instance to be set to NULL at some point after exiting NativeActivity. – lost_bits1110 Sep 21 '11 at 17:46
  • I hope it was because the downvoter mistakenly hit the down arrow instead of up – Miserable Variable Sep 21 '11 at 17:48
  • @lost_bits1110 I don't see how it can revert to null if it is private, unless is being set to `null` somewhere else in the same class. Reflection can be used to set it back to null. But in that case it would be set to null even if it is `public`. – Miserable Variable Sep 21 '11 at 17:50
  • Hmm I don't think I am using reflection anywhere? I also tried setting watchpoints - but Eclipse never breaks when the value is changed. I also put a `finalize` method in `MyJavaSingleton` with some print statements and a breakpoint, this never gets hit either! :S – lost_bits1110 Sep 21 '11 at 18:05
0

"Android OS can and will terminate your singleton and not even tell you about it."

Source: http://www.2linessoftware.com/2010/08/03/singletons-and-services-and-shutdowns-oh-my/

The author of that article suggests some solutions, such as using a Service, or subclassing the Application class. There seems to be quite a bit of support for the latter:

How to declare global variables in Android?

Community
  • 1
  • 1
bmaupin
  • 14,427
  • 5
  • 89
  • 94