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.
Related
The code is like below:
public class MyTestHelperHelper {
static MyTestApi storeApi = null;
public static synchronized void initial(Application application) {
if (storeApi == null) {
storeApi = new MyTestApiImplImpl();
storeApi.initiImplal(application);
}
}
public static MyTestApi getInstance() {
return storeApi;
}
}
MyTestHelperHelper's initial method is called in Application's onCreate, which is in UI thread. MyTestHelperHelper's getInstance method is called in the Activity's onCreate, which is also in UI thread.
In most case, it works normally. But sometimes, it return null with MyTestHelperHelper's getInstance, which leads to the NPE when I do further operations.
Though the case is rare, I can see it several times in the crash report.
I just don't quite understand why:
There is no write to the "storeApi", except in the initial method.
Both initial and getInstance is in main thread, because initial is called in Application onCreate, and getInstance is called in Activity onCreate.
If my app is in background and process is killed, I think the application will be re-created when bring to foreground, so the initial is called.
Seems the NPE occurs only when
1.process starts without application's oncreate. OR
2.When memory is low, many process data, including static variables, classloaders, classes, etc, are cleaned, except the application instance stays in the process. Is it possible?
It is not possible that a process is newly created and Application.onCreate() is not called.
Whenever a process is created, Application.onCreate() will be called.
However there are certain scenarios where lifetime of static variables could be impacted.
Lifecycle of static variable starts with class been loaded by the JVM and ends with class being unloaded.
A static variable will remain alive as long as one of following won't happen:
The class is unloaded due to low memory conditions.
Note: In this case Application object still persists since it will be the last object to be deallocated. This deallocation cannot be controlled by apps but determined by OS. This could happen if your app is in background and OS wants to relinquish the memory.
The process is killed -> Both static object and Application object are deallocated.
You can use onSaveInstanceState() and onRestoreInstanceState() to save and restore state of your static variable respectively.
I have an application works well in emulator and mobile but if we close the application by clicking on exit button of the phone(not from application).and after few hours we are reopening the application, it gets opened from middle of the application(not from the first screen).and after using that app some times it gets hanged and message is displayed 'unfortunately app has stopped '. Is this mobile problem or application problem.
I suggest reading the Activity documentation.
The Android OS has its own application lifecycle management.
Each activity is kept "alive" until its onDestroy is called. For example, the OS can keep an activity alive for several hours and then kill it when there is not enough memory to perform other tasks.
What happens in you case is most likely that the same activity re-runs when you open you app again (in the emulator the activity is probably killed before) and you're in a bad state since probably some of the objects were disposed or re-initialized.
The right thing to do is use some of the other state callbacks, such as onPause/Resume to allocate/dispose resources used by the activity.
You code might look like this:
public class SomeActivity extends Activity
{
public void onCreate()
{
super.onCreate();
// Do some object initialization
// You might assume that this code is called each time the activity runs.
// THIS CODE WILL RUN ONLY ONCE UNTIL onDestroy is called.
// The thing is that you don't know when onDestry is called even if you close the.
// Use this method to initialize layouts, static objects, singletons, etc'.
}
public void onDestroy()
{
super.onDestroy();
// This code will be called when the activity is killed.
// When will it be killed? you don't really know in most cases so the best thing to do
// is to assume you don't know when it be killed.
}
}
Your code should look something like this:
public class SomeActivity extends Activity
{
public void onCreate()
{
super.onCreate();
// Initialize layouts
// Initialize static stuff which you want to do only one time
}
public void onDestroy()
{
// Release stuff you initialized in the onCreate
}
public void onResume()
{
// This method is called each time your activity is about to be shown to the user,
// either since you moved back from another another activity or since your app was re-
// opened.
}
public void onPause()
{
// This method is called each time your activity is about to loss focus.
// either since you moved to another activity or since the entire app goes to the
// background.
}
}
bottom line: always assume the same activity can re-run again.
Actually, that particular application is not closed properly. It is application error only.
I have an activity that looks like the following:
class MyActivity extends Activity {
Runnable refreshTimer = new Runnable() {
public void run() {
refresh();
}
};
protected onCreate(...) {
handler.postAtTime(refreshTimer, ...);
}
protected onDestroy() {
handler.removeCallbacks(refreshTimer);
}
protected void refresh() { ... }
}
After onDestroy is called, there are still messages in the activity's MessageQueue that contain references to MyActivity$0 (the refresh Runnable) for some reason. Because MyActivity$0 has an implicit reference to MyActivity, this causes a memory leak of the MyActivity context.
The result of merge_shortest_paths for android.app.Activity excluding phantom,soft,weak,etc references using Eclipse Memory Analyzer Tool:
(The source code above is a simplification of the actual object relationship displayed in the MAT dump)
Shouldn't calling removeCallbacks remove any references to the runnable objects from the Queue? Why am I leaking contexts?
Something to try:
According to the android docs:
OnDestroy:
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.
When you are exiting your activity, it looks like there are still a bunch of queued up messages and the context for unregistering is not invoking the cancel callback.
What you should do is unregister your runnable in onPause:
This callback is mostly used for saving any persistent state the activity is editing, to present a "edit in place" model to the user and making sure nothing is lost if there are not enough resources to start the new activity without first killing this one. This is also a good place to do things like stop animations and other things that consume a noticeable amount of CPU in order to make the switch to the next activity as fast as possible, or to close resources that are exclusive access such as the camera.
Typically a Receiver or "Scheduled" Runnable will register in onResume, and unregister in onPause for better lifecycle pairing
Without seeing what you are doing in refresh, it is tough to tell, it could be leaking due to activity references that are activity scoped referenced in the refresh method.
I have the following Activity:
public class StartActivity extends Activity
{
String str = "somestring";
int number = "1";
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Code here
}
}
I read the following on the Android docs (http://developer.android.com/reference/android/app/Activity.html)
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.
Does this mean that my class instances (str and number) are "alive" and available unless onDestroy is called or memory is needed after onPause or onStop is called?
If in your caller activity, you have called finish() method, then no instances of previous activity will be alive since you have finished caller activity. All the instances will be garbage collected.
But if your caller activity, does not call finish(), then the activity will no longer be visible but, it will be present on the activity stack maintained by the operating system. The caller activity (i.e. the previous activity) may get finished in cases when the device is low in memory like in cases, ex.: if the activity calls Camera (which requires rich resources), in that case the previous activity may get destroyed.
So in your case, the variables will be alive even if StartActivity is not the present visible activity.
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.