1

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 :).

ShahrozKhan91
  • 710
  • 10
  • 19
  • ovbiously, do not store Activity reference at all ... *obviously there are like so many places we would have tried to access the activity instance* .... sorry, it is not obvious for me why you wana do something such stupid like this ... from service? there is othere way to comunicate between service and activity ... from other acivity? .... – Selvin Mar 27 '15 at 15:32
  • The general way to achieve this is to implement the full lifecycle, if your app have all cases covered, the Activity should recreate itself on start, also you may save the inner state in the SaveInstanceState. Are you really getting this exception or is it only to be prudent? Just do not try to get the same activity again – meyo Mar 27 '15 at 15:36
  • @Selvin i think i didn't put it right, i wanted to say reference and i said instance – ShahrozKhan91 Mar 27 '15 at 16:03
  • 1
    off-topic: as long as instance is not a primitive(then "value" is more accurate) you can use instance/reference/object alternatively (well, in java's world) – Selvin Mar 27 '15 at 16:09

3 Answers3

9

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….

Martin Marconcini
  • 26,875
  • 19
  • 106
  • 144
  • this is exactly what i am doing, but writing it everywhere kind of feel redundant. Because not on "onPostExecute" but cases like fragment transition all require activity reference. – ShahrozKhan91 Mar 27 '15 at 16:08
  • "As a general rule, an Android context should be stored as little as possible and used only when needed." Its really hard to prevent this from happening, for example, a lot of method in android requires context as a param (e.g., ContextCompat.getColor(), getting resources etc.) and when you handle context on some callback call after running asynchronously is a total nightmare. – Neon Warge Apr 06 '17 at 07:10
  • 1
    @NeonWarge You can almost always use an `ApplicationContext` (which leaks no views). If you really need a context to inflate views (or things that are theme-dependent, and there are some), then you shouldn’t be storing the context to begin with, because you are in either an activity or a fragment, and therefore, have instant access to the correct Context. – Martin Marconcini Jul 27 '17 at 22:02
1

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).

Community
  • 1
  • 1
Ed Manners
  • 459
  • 4
  • 8
0

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.

meyo
  • 118
  • 7
  • 1
    This is not only about android life cycle. Lets just take an example in "onCreate" method i started a asynctask and by the time it has reached "onPostExecute" i have left the activity. In "onPostExecute " i am ruing a "runOnUiThread" it takes activity reference. Now the ref would be null. I can obviously check for ref being null but its too redundant. – ShahrozKhan91 Mar 27 '15 at 16:09
  • I see, then Martín Marconcini's answer is wide more specific and oriented to your issue, check it out 'cause it is real good, and also try to get a higher Context as he suggests. – meyo Mar 27 '15 at 16:14