ShahrozKhan91
ShahrozKhan91

Reputation: 710

Handling "Activity has been destroyed" (java.lang.IllegalStateException) exception?

I am looking for a generic way to cater this exception. This can always occur if a user for example keep switching activities like really quickly. Because obviously there are like so many places we would have tried to access the activity reference and when that's null, app would crash with this exception. One way is to check "isFinishing()" every time i access a activity refrence, I want to know if there is another better and generic way to tackle this issue on application level.

Forgive me if my question sound stupid or doesn't make any sense but i guess there is no harm in asking :).

Upvotes: 1

Views: 5316

Answers (3)

Martin Marconcini
Martin Marconcini

Reputation: 27246

As a general rule, an Android context should be stored as little as possible and used only when needed.

If you're getting null or invalid exceptions when trying to use the Activity context, that means you're performing operations outside the standard Android lifecycle of an Activity.

Due to the nature of the lifecycle (asynchronous), it's sometimes really hard to predict when these situations will arise… unless, you avoid performing operations outside the lifecycle events where the context is guaranteed to be alive.

E.g.: Performing Activity/Context operations in onPostExecute methods of asynctasks or even threads, is a time bomb.

As a general rule, before attempting to use an Activity/Context outside the lifecycle methods (onResume for example), is also dangerous and should always be accompanied by a null check.

I used to have a simple method to check this:

if (activity != null && !activity.isFinishing()) {
   // safe
}

After Jelly Bean (API 17 afaik) you can also check activity.isDestroyed() or similar (can't remember now).

If you have to store a context (to later perform some context related action), always try to store the Application Context (activity.getApplicationContext()) which is a static reference to the Application singleton and it won't leak.

That being said, keep in mind what limitations each type of context has. When in doubt, keep a bookmark to this around, specially to understand why trying to inflate layouts with an Application context may yield unexpected results.

UPDATE:

If you need a common/generic place to operate on your Fragments, keep a handy util class around like (pseudo code):

public final class FragmentUtils {
    private FragmentUtils() {
    }
    public static void add(FragmentActivity fragmentActivity, int layoutId, Fragment fragment) {
        if (isContextInvalid(fragmentActivity)) {
           return
        }
        FragmentTransaction fragmentTransaction = fragmentActivity.getSupportFragmentManager().beginTransaction();
        fragmentTransaction.add(layoutId, fragment);
        fragmentTransaction.commit();
    }

    public static void replace(FragmentActivity fragmentActivity, int layoutId, Fragment fragment) {
        if (isContextInvalid(fragmentActivity)) {
           return
        }
       // TODO: you do this one ;)

    }

    public static void remove(FragmentActivity fragmentActivity, Fragment fragment) {
        if (isContextInvalid(fragmentActivity)) {
           return
        }
        // TODO: you do this one ;)
        if (fragment.isAdded()) {
            …
        }
    }

    public static void show(FragmentActivity fragmentActivity, Fragment fragment) {
        // TODO: you do this one ;)
        if (fragment.isAdded()) {
            …
        }
    }

    public static void hide(FragmentActivity fragmentActivity, Fragment fragment) {
        // TODO: you do this one ;)
        if (fragment.isAdded()) {
            …
        }
    }
    public boolean isContextInvalid(final Context context) {
        if (context == null || context.isFinishing()) {
            return true;
        }
        return false;
    }
}

And add the null/checks to your context there. (Or similar) Note the above code is not complete, I just wrote it here in this editor….

Upvotes: 9

meyo
meyo

Reputation: 116

In Android platform, the GC (Garbage Collector) gets rid of unused resources when leaving an Activity (even when you don't expect to), so you save the instance, but the Activity is no longer there, you NEED to think accordingly the Android Lifecycle.

The GC is not predictable, because if you are actually using an Activity it won't disappear, but as soon you stop seeing it, it is elegible for being collected (erased) at anytime.

The basics of Android system include implementing the lifecycle's methods precisely to be able to clean memory at anytime without having the issue of getting a destroyed Activity, instead it is recreated, and if applicable, load a previously saved state.

Upvotes: 0

Ed Manners
Ed Manners

Reputation: 559

Its all about how you model the data in your application. The activity instance data is only temporary, so you should review the following alternatives to see which is more appropriate in your context:

  • You have session level data, this could be maintained in a separate class, possibly a singleton or in the Application object.

  • If you have data you need to persist consider Android preferences or a database.

  • If you are looking to share data (that isn't at the application level) between activities, the callee Activity should pass that via an intent.

From an OO perspective think about who is the rightful owner of that data, or is temporary data that is private to the activity alone (and won't be accessible to others).

Upvotes: 1

Related Questions