I've read that it is a mistake and a source of memory leaks in Android application to keep a long-lived references to a Context.
But I don't understand if it is ok to create a class that looks like this one:
public class HelperClass {
private Context context;
public HelperClass(Context context) {
this.context = context;
}
public void myHelperMethod() {
// uses this.context
}
}
And call it from an Activity:
public class MyActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
HelperClass h = new HelperClass(this);
h.myHelperMethod();
}
...
}
This is fine, and will not cause a memory leak.
As soon as onCreate finishes executing, h will be out of scope and become eligible for garbage collection. If h was static, then you would run into problems. Only when the reference to the context outlives the lifecycle of the context itself will a memory leak occur. A few helpful hints:
Use Context.getApplicationContext() when possible. This context will live as long as your application is alive.
Be careful when using static fields and inner classes.
Run your application through a profiler to check for leaks.
The scope of the HelperClass is only within your onCreate function, so once onCreate executed, your "h" object is no longer needed and subject to garbage collection.
It would be a different story if "h" was a static member - THAT would be a great way to leak memory.
Related
First of all I'd like to say sorry if my question is dummy, I'm just starting with Android. I found some article on the web that states that the singleton that references activity is causing a memory leak.
I cannot understand how this happens!
Imagine such a situation - we have an interface called MyInterface, have a singleton called MySingleton and an activity which implements MyInterface
interface MyInterface {
void foo();
}
class MySingleton {
static MySingleton shared;
MyInterface delegate;
private MySingleton() {};
MySingleton getShared() {
if(shared == null)
shared = MySingleton();
return shared
}
void setDelegate(MyInterface delegate) {
this.delegate = delegate;
}
class MyActivity extends AppCompatActivity implements MyInterface {
#Override
void foo() {//do something}
#Override
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MySingleton.getShared().setDelegate(this);
// do other setup
}
Since Java doesn't have cyclic reference there shouldn't be a problem with garbage collecting MyActivity , or I'm wrong ? Again thanks if the question is dummy and thank you
I don't see any memory leak happening in your code.
In Java:
Memory Leak happens when Garbage Collector thinks an object is still
needed because it is referenced by another object.
Common Sources of memory leak:
Anonymous Class
Inner Classes
Callback methods
Static Variables with a reference to Activity or Fragment
RxJava
AsyncTask
TimerTask
Handlers
Singleton
If a singleton holds a reference to activity and lives longer than the
activity, your app faces a memory leak.
In your situation that happens because of, your activity lifecycle is longer than MySingleton, so your singleton has lifecycle as Activity, but at most situations(depends on architecture, lats lay that you have single activity app) Activity lifecycle will be as all process, or your running app.
There are planty articles about memory leaks in android.
Check them please:
https://proandroiddev.com/everything-you-need-to-know-about-memory-leaks-in-android-d7a59faaf46a
https://android.jlelse.eu/9-ways-to-avoid-memory-leaks-in-android-b6d81648e35e
I'm creating a module (a group of classes to perform functionality) where I need the context object to perform some operations like secure setting read and others.
I do not like to create multiple objects for my classes which ideally is not needed in my case even so i'm doing something like this,
class MyModuleFactory {
private static final String TAG = MyModuleFactory.class.getSimpleName();
public Context mContext;
private static HashMap<Context, MyModuleFactory> myInstance = new HashMap<>();
private MyModuleFactory(Context context) {
mContext = context;
}
public static synchronized MyModuleFactory getInstance(Context mContext) {
if(mContext == null) {
Log.i(TAG, "Context cannot be null");
return null;
}
if(myInstance.get(mContext.getApplicationContext()) != null) {
return myInstance.get(mContext.getApplicationContext());
} else {
MyModuleFactory myModuleFactory = new MyModuleFactory(mContext);
myInstance.put(mContext.getApplicationContext(), myModuleFactory);
}
}
}
My worry here is since I'm holding the context of application here I'm afraid that I could cause memory leak - the reason is because Android can clear up application object and recreate it at anytime. So holding the context behind the application's lifecycle and not allowing to clear the context here could cause memory leak.
What is the better way to force singleton for my module here and also avoiding memory leak.
There won't be any memory leak if you are holding a reference to Application context. Application context lives as long as the app is running. It gets destroyed when the app is killed. In that case, even your singleton will be destroyed, so no memory leak will happen.
Just make sure you are holding reference only to the Application context in the singleton, not Activity context.
Check this post for detailed information:
If you have to create a singleton object for your application and that
object needs a context, always pass the application context.
If you pass the activity context here, it will lead to the memory leak
as it will keep the reference to the activity and activity will not be
garbage collected.
I'm in a little bit of a dilemma and I hope you guys can help me with it.
As you can see I have an AsyncTask in which I have some code to save Bitmap objects as .jpg file to the gallery. In the AsyncTask I'm also using a Context, but as I understand using a context of an Activity in this inner class can cause memory leak, so I changed it to a WeakReference<Context> weakContext; so the garbage collector can collect it.
But by using the Application context that I get from the passed View from the constructor I should archive the same effect as the weak context reference
So is any better to use than the other in this case?
public class ViewToBitmap {
private View view;
private WeakReference<Context> weakContext;
public ViewToBitmap(#NonNull View view) {
this.view = view;
}
// This?
private WeakReference<Context> getContext() {
weakContext = new WeakReference<>(view.getContext());
return weakContext;
}
// Or This?
private Context getContext() {
return view.getContext().getApplicationContext();
}
private class AsyncSaveBitmap
extends AsyncTask<Void, Void, Void>
implements MediaScannerConnection.OnScanCompletedListener {
#Override
protected Void doInBackground(Void... params) {
//TODO: Save bitmaps to gallery
//CONTEXT IS USED HERE
getContext().get()
return null;
}
}
Since the View object has explicit references to Context which was used upon view's inflation, you are effectively keeping a "transitive" hard reference to Context in instances of ViewToBitmap by keeping a hard reference to View.
Also, since AsyncSaveBitmap is not static, instance of this class has implicit reference to the enclosing instance of ViewToBitmap.
The net result is that as long as AsyncSaveBitmap exists, there is a chain of hard references to Activity that will prevent GC of that Activity.
So, the answer is: neither approach is good enough.
The best approach would be to refactor the logic in such a way that no long running code has references to Context, Activity, View, etc.
The most straightforward way of achieving this is to use Observer design pattern or Publish-Subscribe design pattern - this way you could "unregister" in life-cycle methods (e.g. onStop()), thus removing the potentially dangerous reference and preventing memory leaks.
EDIT:
For library purposes, where you don't necessarily need a specific Context and application's Context will suffice, the following patterns can be used (depending on whether your library exposed as Singleton or not):
// Use this approach if clients will use your library as Singleton
private static Context sAppContext;
public static void init(Context context) {
sAppContext = context.getApplicationContext();
}
// Use this approach if clients will instantiate your library's object on each use
private final Context mAppContext;
public MyLibraryEntryClass(Context context) {
mAppContext = context.getApplicationContext();
}
Please clarify is code listed below would lead to context memory leak? Thanks
public class HelperClass {
private Context context;
public HelperClass(Context context) {
this.context = context;
}
public void myHelperMethod() {
// uses this.context
}
}
public class MyActivity extends Activity {
private HelperClass helper;
public void onCreate(Bundle savedInstanceState) {
helper = new HelperClass(this);
}
}
The short answer is no. What you want to beware of is a reference to the context/activity that remains after the activity has been destroyed. A device rotation is an example of an action that would cause the current activity to be destroyed by Android.
In this case, your activity is the only thing that holds a reference to your helper class. Thus when the activity is destroyed, there will be no more existing references.
If however your activity class defined the reference to the helper as a static, then that would cause a memory leak. The static reference would remain even after the activity instance was destroyed. The helper class, which holds a reference to the activity, would then prevent the activity instance from being garbaged collected.
I am newbie to both Java and Android, and currently I am confused about "memory leak" in Android, for example: I have 01 Class, 01 Activity and 01 Interface as following:
Class BackGroundWorker is a singleton, which lives as long as the application lives:
public class BackGroundWorker {
private IOnEventOccurListener listener = null;
private static BackGroundWorker instance;
// ....
public void setListener(IOnEventOccurListener pListener) {
this.listener = pListener;
}
// ....
public static BackGroundWorker getInstance() {
//...
return instance;
}
}
The Listener Interface:
public interface IOnEventOccurListener {
public void onEventOccur();
}
And the Listener itself (An activity):
public class ShowSomething extends Activity implements IOnEventOccurListener{
BackGroundWorker bgWorker;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bgWorker = BackGroundWorker.getInstance();
bgWorker.setListener(this);
}
#Override
public void onEventOccur() {
// TODO do something
}
}
Now, according to what Romain Guy mentioned here:
It’s a memory leak, because there’s a reference to the listener (Activity). So Java GC cannot collect the Activity, even when it’s not in use.
I was able to solve that problem by WeakReference – but still wonder:
In this case, when the device needs more memory, according to Android Dev document, it will “kill” the activity if needed - assuming that the Activity ShowSomething is “killed” – then what happens ? (It’s still leak according to Romain Guy, and still “killed” )
I am really confused. Could anybody please explain this ?
Thank you in advanced,
Son
Android will destroy activities that are not on the screen to try to free up memory. However, GC rules still apply, and hence your static reference to the activity prevents the memory from being freed.
Eventually, Android will terminate the whole process. At that point, your leaked memory will be freed. However, in between your activity being destroyed and the process being terminated, you are wasting RAM.
Rather than use WeakReference, please null out the static reference when the activity is destroyed.
I think it would have the leak if we will use:
static BackGroundWorker bgWorker;
instead:
BackGroundWorker bgWorker;