Warning: Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)
Android Studio:
Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)
So 2 questions:
#1 How do you call a startService
from a static method without a static variable for context?
#2 How do you send a localBroadcast from a static method (same)?
Examples:
public static void log(int iLogLevel, String sRequest, String sData) {
if(iLogLevel > 0) {
Intent intent = new Intent(mContext, LogService.class);
intent.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
mContext.startService(intent);
}
}
or
Intent intent = new Intent(MAIN_ACTIVITY_RECEIVER_INTENT);
intent.putExtra(MAIN_ACTIVITY_REQUEST_FOR_UPDATE, sRequest));
intent.putExtra(MAIN_ACTIVITY_DATA_FOR_VIEW, sData);
intent.putExtra(MAIN_ACTIVITY_LOG_LEVEL, iLogLevel);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
What would be the correct way to do this without using mContext
?
NOTE: I think my main question might be how to pass context to a class from which the calling method lives.
Just make sure you pass context.getApplicationContext() or call getApplicationContext() on any context that is passed in via methods/constructor to your singleton if you decide to store it in any member field.
idiot proof example (even if someone would pass in an activity it will grab the app context and use that to instantiate the singleton):
public static synchronized RestClient getInstance(Context context) {
if (mInstance == null) {
mInstance = new RestClient(context.getApplicationContext());
}
return mInstance;
}
getApplicationContext() according to the docs: "Return the context of the single, global Application object of the current process."
It means that the context returned by "getApplicationContext()" will live through the entire process and thus it does not matter if you store a static reference to it anywhere since it will always be there during the runtime of your app (and outlive any objects/singletons instantiated by it).
Compare that to the context inside of views/activities holding large amounts of data, if you leak a context held by an activity, the system won't be able to free that resource which obviously is not good.
A reference to an activity by its context should live the same life cycle as the activity itself, otherwise it will hold the context hostage causing a memory leak (which is the reason behind the lint warning).
EDIT: To the guy bashing the example from the docs above, there's even a comment section in the code about what I just wrote about:
// getApplicationContext() is key, it keeps you from leaking the
// Activity or BroadcastReceiver if someone passes one in.
Simply pass it as a parameter to your method. There is no sense in creating a static instance of Context
solely for the purpose of starting an Intent
.
This is how your method should look:
public static void log(int iLogLevel, String sRequest, String sData, Context ctx) {
if(iLogLevel > 0) {
Intent intent = new Intent(ctx, LogService.class);
intent1.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
ctx.startService(intent);
}
}
Update from comments on question: Cascade the context from the initiating activity (via constructor parameters or method parameters) right up to the point you need it.
Use WeakReference
to store the Context in Singleton classes & the warning will be gone
private WeakReference<Context> context;
//Private contructor
private WidgetManager(Context context) {
this.context = new WeakReference<>(context);
}
//Singleton
public static WidgetManager getInstance(Context context) {
if (null == widgetManager) {
widgetManager = new WidgetManager(context);
}
return widgetManager;
}
Now you can access Context like
if (context.get() instanceof MainActivity) {
((MainActivity) context.get()).startActivityForResult(pickIntent, CODE_REQUEST_PICK_APPWIDGET);
}
It's just a warning. Don't worry. If you wanna use an application context, you can save it in a "singleton" class, which is used to save all of singleton class in your project.