Question

I have an android (4.1) application which reportedly (can't recreate) chrashes with the message "app as stopped". The problem, however, is that the user has to press "OK" in the alert that pops up. The chrash only occur when the app is not active (on screen). This indicates that Android kills of my app because of memory or naughtiness. I'f been investigating for memory leaks, because i handle bitmaps in the app, that did not pay off.

I have a catch and log all default handler like this:

Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
            public void uncaughtException(Thread thread, Throwable ex) {
                Log.e(StaticData.LogTag, "Unhandled exception app", ex);
            }
        });

To log all exceptions. Afterwards i call original exceptionhandler. This handler is put on the Apps main activity. The method is never called when the "stopped" chrash happens, but is in other cases.

My app uses IntentService to send data to a server in background. This is not a long running service, 1-10s. I will try to put a default exception handler on the service as well. I mention the service because the app is killed when "off screen", so I thought that might have a connection to the problem, but the cause evades me.

Furthermore I use BroadcastReceiver to notify the apps main activity about network connection changes, because the app is used in turbulent network conditions. This is relevant because I'f seen BroadcastReceiver mentioned when people talk about possible memory leak issues. My implementation of BradcastReceiver goes like this:

Serivce side:

sendOrderedBroadcast(uploadedIntent, null);

Activity side:

public static class NetworkStateReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent in) {
        // super.onReceive(context, intent);
        Log.d(StaticData.LogTag, "Network connectivity change");
        if (in.getExtras() != null) {
            NetworkInfo ni = (NetworkInfo) in.getExtras().get(ConnectivityManager.EXTRA_NETWORK_INFO);
            if (ni != null && ni.getState() == NetworkInfo.State.CONNECTED) {
                Log.i(StaticData.LogTag, "Network " + ni.getTypeName() + " connected");
                ...                 
            }
        }
        if (in.getExtras().getBoolean(ConnectivityManager.EXTRA_NO_CONNECTIVITY, Boolean.FALSE)) {
            Log.d(StaticData.LogTag, "There's no network connectivity");
        }
    }
}

As i mentioned in the beginning, the problem is mainly an annoyance for the user because he has to press ok on a popup when looking at mails or taking a call. The app is robust enough to handle that it gets killed from time to time, however, I would like to figure out why my app is knocked out.

PS. I have tried to get the users to send be bug reports via mx log logcollector, but no cigar.

Was it helpful?

Solution

I seem to have solved this problem. I think the culprit was a static reference to the main activity in the application. I was using this static to get a reference to the applications Context. This however this is unnecessary in Service and BroadcastReceiver, because they have their own ref to a "Context".

Why the long face?

A static reference to a Android view, like Activity, is kept in memory and can't be freed by the garbage collector. This was the idea behind making it static. I wanted to have a future reference to the object, but is the wrong approach.

When changing orientation (eg.), Android recreates the current activity (and children views), and the old is freed for GC. When you have a static ref to a view, its not collected, and now you have 2 instances of the Activity. Do the math and realize that this obviously fills memory over time (leak) and Android will nuke the app at some point.

When facing this problem you have to realize that you have to carry your data/states over to the new Activity, for example by using shared memory, a local database or a savedInstanceState:

@Override
public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null) {
       int value = savedInstanceState.getInteger("key");
    }
}
@Override
protected void onSaveInstanceState(final Bundle outState) {
    outState.putInteger("key", value);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top