Which Context should I use in Android?

For novice developers, just simply wrapping your head around what a Context is can be challenging enough and for more experienced developers which Context to use in a given scenario can often be confusing. Using the wrong one can result in memory leaks and styling problems.

The confusion stems from the fact that there are numerous ways to access Context, with (on the surface) no discernible differences. Below are four of the most common ways you may be able to access Context in an Activity.

  • getContext()
  • getBaseContext()
  • getApplicationContext()
  • getActionBar().getThemedContext() //new

What is a Context?

I personally like to think of Context as the state of your application at any given time. The application Context represents a global or base configuration of your application and an Activity or Service can build upon it and represents a configuration instance of your Application or a transitive state for it.

If you look at the source for android.content.Context, you see that Context is an abstract class and the comments on the class are as follows:

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.

What I take away from this is that Context provides a common implementation to access application level as well as system level resources. Application level resources may be accessing things like String resources [getResources()] or assets [getAssets()] and system-level resource is anything that you access with Context.getSystemService().

As a matter of fact, take a look at the comments on the methods and they seem to reinforce this notion:

  • getSystemService(): Return the handle to a system-level service by name. The class of the returned object varies by the requested name.
  • getResources(): Return a Resources instance for your application’s package.
  • getAssets(): Return a Resources instance for your application’s package.

It may be worth pointing out that in the Context abstract class, all of the above methods are abstract! Only one instance of getSystemService(Class) has an implementation and that invokes an abstract method. This means, the implementation for these should be provided mostly by the implementing classes, which include:

  • ContextWrapper
  • Application
  • Activity
  • Service
  • IntentService

Looking at the API documentation, the hierarchy of the classes looks like this:

Context
| — ContextWrapper
|— — Application
| — — ContextThemeWrapper
|— — — — Activity
| — — Service
|— — — IntentService

Since we know that Context itself is not providing any insight, we move down the tree and take a look at the ContextWrapper and realize that there isn't much there either. Since Application extends ContextWrapper, there isn't much to look at over there either since it doesn't override the implementation provided by ContextWrapper. This means that the implementation for Context is provided by the OS and is hidden from the API. You can take a look at the concrete implementation for Context by looking at the source for the ContextImpl class.

ContextThemeWrapper

The first real insight comes in when you at the code for ContextThemeWrapper. Take a look at the following methods:

@Override 
public Resources getResources() {
if (mResources != null) {
return mResources;
}
if (mOverrideConfiguration == null) {
mResources = super.getResources();
return mResources;
} else {
Context resc = createConfigurationContext(mOverrideConfiguration);
mResources = resc.getResources();
return mResources;
}
}
@Override public Resources.Theme getTheme() {
if (mTheme != null) {
return mTheme;
}
mThemeResource = Resources.selectDefaultTheme(
mThemeResource,
getApplicationInfo().targetSdkVersion);
initializeTheme();
return mTheme;
}
@Override public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = LayoutInflater.from(
getBaseContext()).cloneInContext(this);
}
return mInflater;
}
return getBaseContext().getSystemService(name);
}

What you hopefully realize is that anything that extends ContextThemeWrapper uses your theme when returning System resources and application resources. Also, you see that ContextThemeWrapper implements something called getBaseContext() which is used within the context.

Looking through the ContextThemeWrapper, I realize 3 things.

  • Anything that extends ContextThemeWrapper will always be Activity or a class that subclasses activity
  • ContextThemeWrapper takes theme into account when accessing system services and application resources.
  • I would avoid using getBaseContext()

Why I would avoid using getBaseContext()

BaseContext effectively returns which ever context is being wrapped by ContextWrapper. By looking at the code, I can say that this is likely an Activity or Application however ContextWrapper has over 40 known direct and indirect children. The problem is that this means that what the method returns may be ambiguous and I would rather use getContext() or the Activity, FragmentActivity, ActionBarActivity etc. directly, so that I know what I’m holding on to and that I’m holding a reference to something that can cause a memory leak. Also, I have the additional benefit of being able to take advantage of methods that are provided by these classes.

Side note: If you’re using Calligraphy, a very popular Android library for handling fonts in Android, this library works by wrapping base context and setting itself as the new base context. Look at the section in the readme that says “Inject into Context”. By setting itself as a ContextWrapper, it basically intercepts your Views before they are themed and adds the font support to them (yes, this is an over simplification).

ContextThemeWrapper will apply your application theme

The snippet of code I've shared above does not make this immediately clear, however, you can write your own tests to verify this. The best way I can think of doing this is to create a simple app with a ListView that uses android.R.layout.simple_list_item_1 to display items. Make your apps theme Light by default and then initialize your ArrayAdapter using getApplicationContext(). You’ll notice that the text is either not visible or barely visible because it will be white by default whereas your theme should call for default text to be black. Change the code, so that your code initializes the array adapter with “this” (i.e the Activity) or simply getContext() and you’ll see the text turns black.

When should I use getApplicationContext()

getApplicationContext() returns an instance of your Application class. Application is used to maintain a global state for your application. You may or may not have provided your own implementation of this class.

Should you use Application context? Ask yourself the following questions:

  • Is the object that is accessing context long living? Holding a reference to Activity in a long living object or thread can cause a memory leak. In this case, use application context.
  • Are you accessing any system services or application resources that should be themed? For instance, inflating Views or fetching tinted drawables? If you are fetching something that should be themed do not use application context.

In addition, it’s worth pointing out the following:

  • Do not use getApplicationContext() with LayoutInflater, unless you really do want to ignore your theme.
  • When unsure, try getApplicationContext() first.

When should I use getThemedContext()

The getThemedContext() method first made it’s appearance in the ActionBar. The main purpose of this method was to provide the correct themed context to Views that you want to insert into the ActionBar. Think about an app that uses a light theme with a dark actionbar. Any text or custom view you put in the ActionBar, needs to be themed for a dark theme. This is where getThemedContext() is useful.

As of writing this, I can’t find any other instances of getThemedContext() outside of ActionBar and ActionBarCompat, however we may see getThemedContext() appear in more places as more and more widgets gain the ability to be themed.

Update: As Nanik Tolaram points out ToolbarActionBar and WindowDecorActionBar also contain a getThemedContext() methods. However, they are both part of an internal package so this really doesn’t affect app developers.

When should we use getContext()

You can use getContext() in pretty much every instance that isn't long living and doesn’t involve inserting Views into your ActionBar. You can probably even use it in long living instances, just be careful to hold it in a WeakReference and to check to see if it’s null.

Theme AlertDialogs

If you’re building dialogs using AlertDialog.Builder then you may have to inflate or build your Views before you have your Dialog object. Since Dialogs can be themed differently from the rest of your app, this presents a problem. For this reason getContext() was introduced in AlertDialog.Builder in API 11.

Using ContextThemeWrapper for parts of your app that have a different theme

What if you have an application that has some Views that should be themed with a dark theme and others that should be themed light? You could ofcourse go ahead and set textColor and background on each of the Views to accomoplish this, or you can wrap your context in a ContextThemeWrapper and supply it with the theme to use to resolve your Views and Resources.

ContextThemeWrapper contextThemeWrapper = 
new ContextThemeWrapper(getActivity(),
android.R.style.Theme_DeviceDefault_Light_Dialog);
LayoutInflater inflater = getActivity()
.getLayoutInflater()
.cloneInContext(contextThemeWrapper);

You can also use the same approach if you want your Dialogs to use a different theme:

AlertDialog.Builder builder = 
new AlertDialog.Builder(contextThemeWrapper);

Finally

I hope this has helped demystify which Context to use for you a bit. Do you think I got something wrong? Do you want to add something to the article? Let me know in the comments.

In order to build great Android apps, read more of my articles.


Yay! you made it to the end! We should hang out! feel free to follow me on Medium, LinkedIn, Google+ or Twitter.