Android Context in non-activity Class – The Secret of ContextWrapper

Android Context in non-activity Class – The Secret of ContextWrapper

One of most frustrating issues I had to figure out when I’ve started to develop for Android was the Context.

Android Context in non-activity Class

Why there are several kind of Context? and more important – How can I call to system API method from a Class when all the important functions are available only from a Context class??

The problem

Let’s take this function inside activity for example (test for AirPlane Mode),
Where isAirPlaneModeOn() is using the getContentResolver() which is Context.method:

public class MyActivity extends Activity {
 
   ...
 
   public boolean isAirplaneModeOn() {
      return Settings.System.getInt(getContentResolver(),
         Settings.System.AIRPLANE_MODE_ON, 0) != 0;
    }
 
}

Or inside a service:

public class MyService extends Service {
 
   ...
 
   public boolean isAirplaneModeOn() {
      return Settings.System.getInt(getContentResolver(),
         Settings.System.AIRPLANE_MODE_ON, 0) != 0;
   }
 
}

P.S: The isAirplaneModeOn() function is using depreceated method (pre API 17) just for the purpose of simple example and backward compatibility.

Now… lets say you want to create some other class, and move the function to that class:

import android.provider.Settings;

public class MyOtherClass {
   ...
   public boolean isAirplaneModeOn() {
      return Settings.System.getInt(<span style="text-decoration: underline;">getContentResolver</span>(),
         Settings.System.AIRPLANE_MODE_ON, 0) != 0;
   }
}

HEY?!!? Why Eclipse telling me: The method getContentResolver() is undefined for the type MyOtherClass

????? ….. Because getContentResolver() is a context method – Soon you realize you must have access to Context.. so What the solution?

 

Some solutions

I’ve read a lot about context(s). Maybe, most of the valuable stuff out there in the internet…

Some are saying to pass the context to the class and save a reference to that context like:

class YourNonContextClass {
   private Context context;

   public YourNonContextClass(Context context){
      this.context=context;
   }

   public boolean isAirplaneModeOn() {
      return Settings.System.getInt(context.getContentResolver(),
         Settings.System.AIRPLANE_MODE_ON, 0) != 0;
   }
}

Some are saying to pass the context to your static function like:

public class MyOtherClass { 
   public static boolean isAirplaneModeOn(Context context) {
      return Settings.System.getInt(context.getContentResolver(),
         Settings.System.AIRPLANE_MODE_ON, 0) != 0;
   }
}

Both solutions works. Both have their Pros and Cons. both require you to alter the code inside the function.

 

Solution – The Secret of ContextWrapper

Finally … After a fashion… With lot of experimentation, I’ve found a great solution that I really like! :

CONTEXTWRAPPER!

So What is ContextWrapper?

Proxying implementation of Context that simply delegates all of its calls to another Context. Can be subclassed to modify behavior without changing the original Context.

In other simple words: It’s a class that inherits another Context.

 

So, How to use the ContextWrapper?

Let’s talk about the function we discussed above and create a ContextWrapper class for it:

public class MyContextWrapper extends ContextWrapper {

    public MyContextWrapper(Context base) {
      super(base);
   }

   public boolean isAirplaneModeOn() {
      return Settings.System.getInt(getContentResolver(),
         Settings.System.AIRPLANE_MODE_ON, 0) != 0;
   }
}

Note that nothing need to be changed in the isAirplaneModeOn() function as the class itself inherits the wrapper you’ve called from (Activity, Service, IntentService, etc).

To call the function just use:

boolean isAir = (new MyContextWrapper(this)).isAirplaneModeOn();

Where ‘this’ could be any context you’d like to. applicationContext, Service or Activity.

 

That’s it.  Easy haa!

And choose what fits you best…

 

28 thoughts on “Android Context in non-activity Class – The Secret of ContextWrapper

  1. Rene

    Wouldn’t the last solution basically be the same as :
    boolean isAir = (new YourNonContextClass(this)).isAirplaneModeOn();
    What are the exact advantages of using a class inherited from ContextWrapper instead of the above?

    Reply
    1. Etay Cohen-Solal Post author

      Yes. it’s exactly the same.
      But inside the class you would write:

      1
      2
      3
      public MyContextWrapper(Context base) {
        super(base);
      }

      Instead of:

      1
      2
      3
      4
      private Context context;
      public YourNonContextClass(Context context){
        this.context=context;
      }

      And calling the context method is shorter (like in activity):

      1
      getContentResolver()

      Instead of:

      1
      context.getContentResolver()

      So, if you move a method from your Activity into ContextWrapper class you don’t need to change a thing (add context. everywhere).

      Reply
      1. Pramod Kumar

        I think, there should be some other benefit because of using this ContextWrapper. Have to explore it a bit deeper

        Reply
        1. Etay Cohen-Solal Post author

          I am sure there is. reading from documentation : Proxying implementation of Context that simply delegates all of its calls to another Context. Can be subclassed to modify behavior without changing the original Context.
          I don’t really know what the mean of “Proxying” and “delegates” and how it’s work. maybe looking in the code will brighten things up.
          The real question is if ContextWrapper help us avoid memory leaks.

          Reply
          1. davud

            Hello, @Etay I think ContextWrapper can not help to avoid memory leaks. Check the ContextWrapper source. it holds a strong reference to context and not releases it.

  2. Nikhil

    @About Etay Cohen-Solal hey thanks. You saved my day. I was literally scratching my head since two hours. Bloody Context. Yours solution is super super simple easy than other solutions. Finally i can use Context in non-activity classes and I can divide the code into separate files for easy code management.

    Reply
  3. nnO

    Hi,
    Using this way i try to sendbroadcast but getting nullpointerException. Here is a code:

    public class ConfigurationManager extends ContextWrapper {

    public ConfigurationManager(Context base) {
    super(base);
    }
    ….
    private void stopVoiceRecognizer() {
    Log.d(“sender”, “Broadcasting voice Recognizer stop message”);
    Intent intent = new Intent();
    intent.setAction(“stop_voice_recognizer_service”);

    (new ConfigurationManager(this)).sendBroadcast(intent);

    }

    Can you please suggest me where i am doing wrong.

    Reply
  4. Sam

    Hello i am using a library function which need activity context but i am calling it using service context so . can proxy service context as activity context for that library fuction

    thanks

    Reply
  5. payam

    currently I’m using the first solution, passing the context or sometimes the Activity itself to the class, but the wrapper seems to be a better way.

    Reply
  6. swati agarwal

    Hi , this solution seems to be great but as you have written

    “To call the function just use:
    boolean isAir = (new MyContextWrapper(this)).isAirplaneModeOn();
    Where ‘this’ could be any context you’d like to. applicationContext, Service or Activity.”

    Can’t this be used with fragments as my application is based of the fragment architecture . And making this i need to pass the context from the fragment itself.
    How should i be doing it then?

    Reply
  7. swati agarwal

    And one more thing if in my fragment i use 5 common functions that i have now placed under the MyContextWrapper then for each function do i need to write like this only:
    (new MyContextWrapper(this)).funcName();

    or we can make something common for this???

    Reply
  8. Felipe

    This approach solves the memory leaks of dialog?
    Example:
    public class Endpoint extends ContextWrapper {
    public Endpoint(Context base) {
    super(base);
    }
    public void makeDialog() {
    // getting context with getBaseContext()
    ProgressDialog dialog = new ProgressDialog(getBaseContext());
    dialog.setMessage(“Blá blá”);
    dialog.show();
    }
    }

    And in activity:
    (new Endpoint(this)).makeDialog();

    So, if I close my app with a dialog open, will throw a memory leak cause my activity is closed or the wrapper will support it?

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.