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.
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.
View
s 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:
getApplicationContext()
for singletons. This type of Context
lives for as long as your application lives - thus for as long as your singleton lives.WeakReference
s. 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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With