2

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.

athos
  • 1,281
  • 3
  • 14
  • 24
  • can't you release that thing in onPause / init it in onResume? There should only be 1 instance that is in this state. Or move the whole thing into a Service since those are always single instances – zapl Apr 23 '12 at 16:53
  • i can't release "that thing" (it's a good way to name it :) ), neither in onPause nor anywhere else, I can just rely on it being safely called after a process kill. The service option sounds like an overkill for my needs, but I am starting to consider it. – athos Apr 23 '12 at 17:04
  • a simple local (Intent)Service is just some lines of code and you can start it in one activity, stop it in the next since its life-cycle isn't tied to an activity. They are great for things like background music that shall not be interrupted. Trying to work around the Activity life-cycle is certainly possible but I wouldn't consider that a clean solution. – zapl Apr 23 '12 at 17:09

3 Answers3

1
  1. 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.
  2. 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();
  }
}
Andrey Starodubtsev
  • 5,139
  • 3
  • 32
  • 46
  • just tried the 1. it seems to work, but will i be sure that pids for different processes will be always different? I mean, there's NO possibility that the new process created after the old one is killed gets the same identical pid? I didn't understand the 2, could you elaborate more? – athos Apr 23 '12 at 18:00
  • 1. You are right, theoretically it's possible that OS killed your process, than you launch huge amount of processes to wrap pid count around, and after that create new one with same pid - in this situation you won't do initializing procedures you need. But this situation has very low probability =) About algorithm used to assign pid for newly created process see, for example, here: http://stackoverflow.com/questions/3446727/how-does-linux-determine-the-next-pid. 2. see answer's update. – Andrey Starodubtsev Apr 23 '12 at 20:04
  • @athos Solution 2 should be the accepted answer. Solution 1 is ugly. – MaciejGórski Apr 30 '13 at 11:32
0

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.

Bharat Sharma
  • 3,926
  • 2
  • 17
  • 29
  • tried this, using the process id. see comment to @darkmist's answer. – athos Apr 23 '12 at 18:00
  • You need not to concentrate on that different process can have same pid. At one time one thread id will be unique. so if your thread have id 1 then at that time other child thread cannot have same id. One more thing if you use Thread.currentThread().getId())); in your oncreate then you will have UIthread id but you need id of that thread in which init function is running so either use that above statement into your init function of just a line above to init function according to your condition. Save it in variable and use it. it should work. – Bharat Sharma Apr 23 '12 at 18:57
0

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.

David Wasser
  • 93,459
  • 16
  • 209
  • 274
  • This will not work if you are on another Activity when this app's process is killed and later restarted. – MaciejGórski Apr 30 '13 at 11:33
  • @MaciejGórski what do you mean "this will not work"? OP asked about a specific case. He needs to make sure that he only calls the `init()` method once within a process. My answer isn't meant as a general purpose "this is how to restore everything in case Android kills your process" – David Wasser Apr 30 '13 at 11:58
  • To make sure native lib is initialized before it is used, it cannot be initialized it in the (first) Activity normally run when user taps application icon. When process is killed and restarted on any other Activity (think Spash, Menu, Game) you cannot use audio API, so there will be no sound or the app will crash. – MaciejGórski Apr 30 '13 at 12:07
  • @MaciejGórski Sure, so in that case each activity will need to check that the library is initialized before using it. But that isn't what OP asked. In any case, maybe OP already has some other code that recognizes that the process has been restarted and redirects to the "splash" activity on a restart. – David Wasser Apr 30 '13 at 12:13