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.
Related
I have recently encountered a problem where sometimes accessing a singleton from a background Service threw a NullPointerException.
The singleton instance is constructed in the launcher activity and is guaranteed to not be null for the duration of my app.
This leads to my questions of how the Service lifecycle is related to the application process's lifecycle. Concretely, here are the specific questions:
When an activity goes into background, will the containing process be swapped out to secondary storage? If so, how does that affect the service running in the background?
When a service is restarted by the android OS, how much of the containing process will be restored? Will the process just become a wrapper for the service?
Update: Here are the code related to my singleton Implementation
private static User curUser = null;
public static User getCurUser(){
return curUser;
}
public static void setCurUser(User u){
curUser = u;
}
There is only 1 place in my app that calls setCurUser(), that is in the launcher activity, which gurantees that the user is non-null before setting it.
Let's assume that I have an Android App with two Activities (Activity1 and Activity2). In Activity1 I declare a public static Boolean foo = true.
When I finish() Activity1 and move to Activity2, I am able to see that "foo" variable has value true
But when the System has low memory (e.g. because there are many apps running on the device) then, when I am on Activity2 I see that the value of "foo" variable is null.
How is this explained?
It's important to note that the life of a static variable is tied to the process not the activity. Even if your activity is destroyed, the static variable will still be alive (which is why you see it's value set to true). It's only when the process is destroyed that the static variable will be freed properly.
This is also one of the reasons you shouldn't use static variables to hold references to activities, contexts, or views. Huge memory leaks waiting to happen.
For your particular scenario, this means that:
Act1 created & set the variable; You've moved from Act1 to Act2
The processes is killed in the BG
When the system attempts to restore you, you end up back at Act2
However, since the initialization of that variable happened in Act1, (which hasn't been initialized) the variable doesn't get set.
If the process is killed then all static variables will be reinitialized to their default values.
So whatever value you have set in Activity1 will not persist
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 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 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.