Context calls

What is it about Context in Android?

Hi,

Today I want to talk about Context in Android. As you know, whether we’re attaching a view or get an asset, – almost always there is a reference to Context. From the documentation, Context is an interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.

On the one hand, we all know it and use it, on the other – we often use it in a wrong way or just don’t care enough whether garbage collector will clean it or will the context be hanging there somewhere or, even worse, cause a memory leakage. So, what are the ways of getting a context? A couple actually:

  • this / getActivity()
  • getContext()
  • getBaseContext()
  • getApplicationContext()

this is just a current Activity we’re in. It’s often used in conjunction with a Toast, e.g.

Toast.makeText(this, "Boom!", Toast.LENGTH_LONG).show();

getContext() belongs to View, so it’s the view’s current context (the activity it resides in)

getBaseContext() is very specific, in a way that it shouldn’t be normally used. It doesn’t mean it’s completely useless though. It just means you have to have a good reason to use it, like when you want to override a context with your own one. I have found a wonderful explanation on this blog post.

getApplicationContext() should be used in places, when you don’t care or don’t have access to activity’s context and all you want is some information in a context of a whole application

What’s more to remember, is that you must be very very cautious not to hold the context longer than it is necessary. Especially it’s true with activities. Unfortunately, we’ve all made sins by creating anonymous inner classes with a strong reference to the Context. Mostly they were AsyncTasks whose execution didn’t stop after activity was killed, thus garbage collector couldn’t clean it up in the right time. Of course, there’s a little more code to write (see Handler example below), in order to be sure Activity‘s context hasn’t leaked, but it is always worth the effort, especially when writing a complex application.

Example of activity’s weak reference from a thread


private static final Handler handler = new Handler();
private final Runnable messageTask = 
		new SetMessageTask(this, "This is the message!");

private void postTask() {
	handler.postDelayed(messageTask, 8000);
}

private static class SetMessageTask implements Runnable {
	private final WeakReference activity;
	pivate final String message;

	SetMessageTask(MainActivity activity, String message) {
		this.activity = new WeakReference<>(activity);
		this.message = message;
   }

	@Override
	public void run() {
    if (message == null || message.isEmpty()) {
        Log.e("SetMessageRunnable", "The message is null!");
        return;
    }

    MainActivity activity = (MainActivity) this.activity.get();
    if (activity == null) {
    Log.w("SetMessageRunnable",
 		"Activity is not available anymore.");
       return;
    }

    // do something with activity instance here...
    }
}

With the addition of fragments, it all became even more complicated, because of their own lifecycle and its connection with activities. This in turn had caused many state and transaction error, also while moving back in stack. I personally, witnessed java.lang.IllegalStateException Crash: Can not perform this action after onSaveInstanceState many times, caused by the creation of new transactions after saving activity status, or before activity recreates.

Sometimes you could’ve had an impression that commiting fragment’s transaction anywhere besides onCreate() will cause an exception. This is not true, because you can also make a transaction in onPostResume() or in onResumeFragments(). Still, those are only three places, which may seem a little limiting. Either this or using commitAllowingStateLoss() method, which should be called very very rarely and when you know exactly what you’re doing (and not only what you want to achieve).

resume methods

Image 1. Activity lifecycle methods and their order of calling

Whatever rules and restrictions API dictates, you must remember that activity can be killed at any possible time by the system, in order to free resources. That is why you cannot always rely on Context and its availability.

Sometimes, there are situations which can be very confusing, for instance, taking care of the view and loading data in the same method (you shouldn’t do that!). Tight coupling can make things dependent on each other in an uncontrolled way. You can be tempted to do everything in onCreateView()… The appearance of Fragments has only caused deeper problems with Context, partly because it wasn’t always until …. method call. Only in onActivityCreated() or onAttach() we can be sure of …. New version of method.

On the screenshot below you can see MainActivity instance leakage, caught by LeakCanary library.

memory leak example

Image 2. MainActivity instance’s memory leackage, caught by LeakCanary

As a sum up, I present a few rules which when followed will help you to get rid of context issues. It’s possible that I have forgotten about something, so please share your thoughts in the comments below 😉 Thanks!

Rules:

  1. Use getContext() or Activity.this when dealing with views inside activities (TextView, Button, Toast, etc.)
  2. Use getApplicationContext() if you need application-level context, which is not tight to any views/activities (for instance, in BroadcastReceiver or Service)
  3. Do not use getBaseContext(). Again, find a great explanation why here.
  4. Make use of WeakReference if you need to access context from inside the threads
  5. Do not reference context in one activity from another activity, and don’t make it static
  6. When in fragment, assign a context to the one from onAttach(Context context) method

UPDATE

There is also getSupportActionBar().getThemedContext() which you should use when inflating anything to the ActionBar, so it’s theme is automatically applied to what you inflate. This was mentioned here.

Like and share:

Published by

Tonia Tkachuk

I’m an Android Developer, writing code for living and for fun. Love beautiful apps with a clean code inside. Enjoy travelling and reading.