Monday, June 9, 2014

Android - Static Access to Context outside of an Activity or Service

I recently got a crash report in my Android app (Megalarm, you should check it out) because the Context object in one of my singleton classes was stale and unexpectedly null. This is a Context object that was cached when the application was first launched at bootup.


To get around this, I started with a trick learned from this question on Stack Overflow.


(decorate your application node in AndroidManifest.xml as such)
<application android:name="com.seandroid.megalarm.MegalarmApplication">
    ...
</application>


(then create the matching application class)
public class MegalarmApplication extends Application
{
 private static Context initialContext;
 
 @Override
 public void onCreate()
 {
  super.onCreate();
  initialContext = getApplicationContext();
 }
 
 public static Context getAppContext()
 {
  return initialContext;
 }
}

This method of getting a Context object statically is good as it works most of the time but doesn't guarantee it won't be null (should getAppContext() be called before it has been created). Nonetheless this works in most cases.


However it doesn't fix my problem where the value was stale and null at one point much later. That's why I supplemented this with another class:

public class ContextHelper
{
 private static final ContextHelper instance;
 private Context latestContext;
 
 static
 {
  instance = new ContextHelper();
 }
 private ContextHelper()
 {}
 
 public static ContextHelper getInstance()
 {
  return instance;
 }
 
 public Context getContext()
 {
  if (latestContext != null)
   return latestContext;
  
  return MegalarmApplication.getAppContext();
 }
 public void setContext(Context ctxt)
 {
  latestContext = ctxt;
 }
}


This class will returned the latest Context object and if that is missing, the Context in the other singleton: MegalarmApplication.

Lastly, I modified my Activity classes on add a call to track the Context in each onResume() implementation.

@Override
protected void onResume()
{
 ...
 super.onResume();
 ContextHelper.getInstance().setContext(this);
}

I hope this helps you out if you've come across a similar problem.