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
Related
I know that this is a bad idea to pass context/activity to another object that may live longer then activity and store it, but in my app I really need this. Here is a pseudocode that I'm using
class MyActivity extends Activity implements ActivityDelegate {
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MySingleton.getShared().setDelegate(this);
}
void onDestroy() {
super.onDestroy();
MySingleton.getShared().setDelegate(null);
}
}
class MySingleton{
private static MySingleton shared;
private ActivityDelegate delegate = null;
public static MySingleton getShared() {
if (shared == null)
shared = new MySingleton();
return shared;
}
void setDelegate(ActivityDelegate delegate) {
this.delegate = delegate;
}
}
As you can see I always set delegate to null in onDestroy, this is why memory leak should not occur. But as far as I know there are some cases when onDestroy is not called. I guess this only happens when the entire app process is purged and the memory get released anyway. So, my question is - is this safe to pass context/activity to another object and then unset it in onDestroy call or it can still cause leaks in some cases ? Thanks
I've stumpled upon an Android Application Class which implements the Singleton pattern and bind a static object to it.
public class App extends Application
{
public static BigObject myObj;
private static App instance;
public static App getInstance()
{
return instance;
}
#Override
public void onCreate() {
super.onCreate();
instance = this;
myObj = new BigObject(this);
}
}
Are there any problems with this implementation, regarding performance, memory leaks or maybe Exceptions, when getInstance().myObj.something() is called form BroadcastReceiver or Service?
The only drawback I see is somewhat ugly code, using dependency injection would be better. I don't know, but if OS guarantees that all other components will be launched after Application::onCreate than there is no issues. Even non-main threads will not cache value of bigObject. But if you want set value of bigObject after onCreate, or it's creation takes long time you can face issues with data racing or slow startup.
I don't see any problems with this implementation. The Application object is basically a singleton.
I want to create a static presenter object in my Activity, so that when the Activity is recreated because of config changes, it will retain the presenter instance and my business logic will not be affected.
The code of my Activity is:
public class HomeActivity extends AppCompatActivity {
public static HomePresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
if (presenter == null){
presenter = new HomePresenter();
}
}
}
Nothing will happen to the static instance. But doing this could leak memory (see Avoiding memory leaks) if you do not delete the reference to the static presenter.
I would suggest another approach. Override onRetainNonConfigurationInstance() to keep an object when the Activity is destroyed because of an configuration change (e.g. rotation). And use getLastNonConfigurationInstance() to get the very same object after the configuration change.
public class HomeActivity extends AppCompatActivity {
public HomePresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
presenter = (HomePresenter)getLastNonConfigurationInstance();
if (presenter == null){
presenter = new HomePresenter();
}
}
#Override
public Object onRetainNonConfigurationInstance() {
return presenter;
}
}
You can also use a Fragment to keep objects alive during a configuration change, see RetainingAnObject.
Your code will work, presenter will be alive, but, please, don't do this.
Keyword 'static' means that value of this field will be attached to class, not to instance of it. So if you for example will have your HomeActivity, then you go to the SomeElseActivity and then go to new HomeActivity (you will have back stack HomeActivity -> SomeElseActivity -> HomeActivity) for new HomeActivity you will have same presenter as for old one. Thus you will have one share presenter for 2 independent instances of HomeActivity. Moreover, if you have some state in presenter, you will have a lot of problems with your application in this case.
I recommend you to remove 'static' keyword. And if your presenter have state, that's needed to be saved during config changes, try one of 2 alternatives:
1) Create onSaveInstanceState and onRestoreInstance state in your presenter and call them in appropriate activity's methods
2) Create fragment without ui, but with flag 'retain instace' (setRetainInstance method), and this fragment will keep reference to your presenter.
In short, the static object remains, and it gets its birth when you class is loaded into memory, and never goes away until your app dies, or when the class is unloaded.
In JVM languages, the compiler optimizes static fields by embedding the value in the bytecode instead of computing the value at runtime.
When you fire up a JVM and load a class for the first time (this is done by the classloader when the class is first referenced in any way) any static blocks or fields are 'loaded' into the JVM and become accessible.
So the static variable lives in the circle in the snapshot, and it is ignorant of whatever config changes, it is there, no matter what happens and as long as as the class is loaded.
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;
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.