Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing `Context` everywhere seems messy - create classes to handle different interactions with context?

There are loads of questions which deal with Context, which context to use, and how to store it, etc. But I feel dirty every time I pass it to an object, or create a static or singleton that provides access to it. I'm not sure what smell I'm getting, but it definitely smells.

I was thinking an alternative would be to create classes that act as a proxy for a context, which I pass around instead, that defines a subset of a contexts' features as a sort of interface (Not the language interface keyword).

An example of an alternative (with code left out for readability):

// in activity.onCreate():

    StateStorer ss = new StateStorer (getApplicationContext());
    RememberMe obj = new RememberMe(ss);
    ss.restore();

// in activity.onDestroy()

    ss.save();

// the "proxy"
class StateStorer {
    List<StateStorerListener> listeners;
    Context mContext;
    public StateStorer(Context context){
        mContext = context;
    }
    public SharedPreferences getSharedPreferences(String tag){
        return mContext.getSharedPreferences(tag, 0);
    }
    public save(){
        // tell listeners to save
    }
    public restore(){
        // tell listeners to restore
    }
}

// an example class that needs to save state

class RememberMe {
    public String TAG = "RememberMe";
    public StateStorer mSs;
    public RememberMe (StateStorer ss){
        mSs = ss;
        ss.addListener(this)
    }
    // this class would implement the StateStorer's listener interface,
    // and when the StateStorer tells it to save or restore, it will use the
    // StateStorer's methods to access the SharedPreferences object
    public void onRestore(){
        SharedPreferences sp = sSs.getSharedPreferences(TAG);
        // restore from prefs here
    }
}

Are there any OOP principles which this goes against? Or smells that it fixes? I just can't decide.

like image 765
CL22 Avatar asked Aug 13 '14 09:08

CL22


1 Answers

Whenever passing a Context instance to another class, think,

"Is it possible that this class will actually live longer than the Context I'm passing to it?"

If the answer is no, don't worry. If the answer is yes, think why.

Views for example, when used normally, will never live longer than your Activity. As soon as the Activity gets garbage collected, your View will get garbage collected, so there's nothing to worry about.

Singletons however, do live longer, and will leak the Context. That is, when the Activity is supposed to be garbage collected, it will not be, because the singleton still has a reference to it.

A couple of solutions come to mind:

  • Use getApplicationContext() for singletons. This type of Context lives for as long as your application lives - thus for as long as your singleton lives.
  • Use WeakReferences. This ensures that you will not keep an active reference to your Context, and avoid leakage. You will however need to compensate for possible nullity of the Context.

Obviously, it is required you understand the basics of garbage collection. Here's an article about that.


As for the example code you've given, I see no difference in passing this instance around than passing the actual Context around. In both cases you hold a reference to the Context. In fact, the StateStorer class seems to be a singleton, and - like you did - should be provided with the ApplicationContext.

You will also often see that singletons, when supplied a Context, call getApplicationContext() on it themselves to avoid such mistakes:

public static MySingleton getInstance(final Context context) {
    if(sInstance == null) {
        sInstance = new MySingleton(context.getApplicationContext());
    }

    return sInstance;
}
like image 50
nhaarman Avatar answered Oct 25 '22 09:10

nhaarman