58

So, my first major application is almost coded and I'm doing optimizations on my code. The app works fine, but I'm not sure about my way of passing the context to other classes. I don't want to do it the wrong way. I stumbled upon articles and questions here in Stackoverflow about contexts and which is the right way to pass it to non-activity classes. I read the documentation as well, but being a Finn makes complicated tech speak even harder to understand.

So, a simple question. Is my way of passing my main activity's context to other (helper) classes correct? If not, where can I read more about better practice on these situations.

For example: MainActivity.java

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle sis){
        super(sis);
        new Helper(MyActivity.this).makeMyAppAwesome();
    }
}

Helper.java

public class Helper {
    Context context;
    Helper(Context ctx){
        this.context = ctx;
    }

    public void makeMyAppAwesome(){
        makeBaconAndEggsWithMeltedCheese(context);
    }
}

Is this OK? It would be nice if someone could provide an easy to read article with examples on this subject.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Iiro Krankka
  • 4,959
  • 5
  • 38
  • 42
  • 5
    The basic rule of thumb is don't hold on to a `Context` longer than the `Context` exists. – Dan S Oct 24 '11 at 19:20
  • 1
    That's one of the sayings I'm not sure I understand. It's obvious I don't use the same context in another Activity, but this isn't probably what you're talking about? – Iiro Krankka Oct 24 '11 at 19:26
  • 3
    The key to this saying, for your example, is understanding the Activity lifecycle. This will prevent memory leaks and using an old instance of Activity (this won't happen in your example). In your helper I would include a way to set the activity null in onStop and setting the value again in onStart. – Dan S Oct 24 '11 at 19:33
  • 8
    What he means is that your Helper-class might live longer then your MainActivity. Which means that there is still a reference to it (= it's context), so it can't be garbage-collected. And you have a memory leak. The best way to avoid this is: Don't store the context. Use an argument instead. Like `new Helper().makeMyAppAwesome(ctx);. This way you have a reference to the context as long as you need it - but you don't have to worry about cleaning all the stored references when the activity gets destroyed. –  Oct 24 '11 at 19:34
  • Thank you. That cleared it up so much. I understand the Activity lifecycle and have already unregistered some receivers and stuff on my onPause but never thought of that. So I could do this right by making Helper.context = null everytime my MainActivity pauses and restore it onResume? Or just like alextsc suggests. – Iiro Krankka Oct 24 '11 at 19:42
  • If I have a public method getContext() in my main Activity, and I call that from my Helper whenever I need the context, would the other class use the context even if my main Activity is paused? – Iiro Krankka Oct 25 '11 at 15:40

4 Answers4

43

You can do that using ContextWrapper, as described here.

For example:

public class MyContextWrapper extends ContextWrapper {

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

    public void makeMyAppAwesome(){
        makeBaconAndEggsWithMeltedCheese(this);
    }
}

And call the non activity class like this from an Activity

new MyContextWrapper(this);
HimalayanCoder
  • 9,630
  • 6
  • 59
  • 60
ET-CS
  • 6,334
  • 5
  • 43
  • 73
  • 1
    Welcome to SO, kudos for actually providing a better answer than exists and is accepted on a popular question in your first post! I've edited your answer just to format the code properly - check out the help link for more info next time you answer. :) – OJFord Jul 26 '14 at 15:00
  • This is just as bad as retaining the context. Be aware that the ContextWrapper will keep a reference to the context. If you are using this then make sure the ContextWrapper you create is properly dereferenced. – Dave Thomas Mar 09 '18 at 15:36
  • @DaveThomas I'm having helper class in application level. If I use this approach and pass application context, will it lead to memory leak? – Madhan Oct 08 '20 at 11:19
  • @Madhan Just pass the context through when it is needed. Never retain it. Any function that needs should accept it as a parameter. See the answer on here detailing that approach. – Dave Thomas Oct 08 '20 at 14:45
4

It is usually in your best interest to just pass the current context at the moment it is needed. Storing it in a member variable will likely lead to leaked memory, and start causing issues as you build out more Activities and Services in your app.

public void iNeedContext(Context context) {...

Also, in any class that has context, I'd recommend making a member variable for readability and searchability, rather than directly passing or (ClassName.)this. For example in MainActivity.java:

Context mContext = MainActivity.this;
Activity mActivity = MainActivity.this;
Gibolt
  • 42,564
  • 15
  • 187
  • 127
0

I have passed context like this which solved my problem:

    public class Utils extends ContextWrapper {
private final Context context;
    
    public Utils(Context context) {
        super(context);
        this.context = context;
    }
public void mymethod(){}
}

super(context); with ContextWrapper helped to make getBaseContext() and getApplicationContext() valid and this.context = context; captured context in variable which I can use wherever needed in methods. Maybe alternatively you can just opt for using a constructor with this.context = context; and replace all occurrences of getApplicationContext() and getBaseContext(). Well, an even better way is to pass context directly to the method if using only few from a class for avoiding memory leaks.

-4

You could also create a static instance reference to your MainActivity initialized in the onCreate() method

public class MainActivity extends AppCompatActivity {

    public static MainActivity mMainActivity;

    @Override
    private onCreate(Bundle savedInstanceState){

    //...

    mMainActivity = this;

    }
}

and call the context like this:

MainActivity.mMainActivity;

or write a method getInstanceOf() if it's clearer and/or you prefer using an accessor

MainActivity.getInstanceOf();

This strategy might provide you with some flexibility if you decide later that you would like to call an instance method contained in your main activity like so:

MainActivity.mMainActivity.myInstanceMethod();

Just a suggestion. Criticism is welcome and encouraged.

the_prole
  • 8,275
  • 16
  • 78
  • 163
  • 3
    public static MainActivity mMainActivity; This will create a global static instance of Activity and will not be garbage collected, hence it will leak memory. – Jay Donga Sep 08 '17 at 07:05