Possible solution for avoiding memory leak from static context reference - android

I know that memory leaks have been nearly done to death on stack overflow, but here goes another question, just to be sure...
I have a singleton class, MyManager which on such-and-such an event notifies listeners that something has changed. This manager manages some 'global' data structures, hence my using it.
public final class MyManager{
private final static MyManager INSTANCE = new MyManager();
private ArrayList<MyManagerListener> mListeners = new ArrayList<MyManagerListener>();
public static void addListener(MyManagerListener l){
if (!INSTANCE.mListeners.contains(l)) INSTANCE.mListeners.add(l);
}
public static void disconnect(){
// Does calling this in Activity's onPause() avoid memory leak?
INSTANCE.mListeners.clear();
}
/// Implementation of Manager stuff which includes call to mListener.doSomething();
}
I then of course have the interface MyManagerListener:
public interface MyManagerListener{
public void doSomething();
}
And then in my Activity, I add the Activity instance to the the manager's mListeners, in my understanding this is creating a static reference to the Activity potentially disrupting the Activity's lifecycle, which is bad.
public class MainActivity extends Activity implements MyManagerListener{
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create potential memory leak here.
MyManager.addListener(this);
...
}
protected void onPause(){
super.onPause();
// does calling this fix the potential memory leak?
MyManager.disconnect();
}
#Override
public void doSomeThing(){
//do something
}
}
My question is, does my inclusion of MyManager.disconnect() address the potential issue? I know calling ArrayList.clear() sets all the objects in the list's underlying array to null

You should clearly remove references in onPause() or onStop() instead of in onResume().
Doing so should remove all references to your activity and therefore prevent memory leaks.

Related

Memory leak even if I release retained objects in onDestroy?

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

Android: Singleton references activity, memory leak?

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

Singleton Destruction, Class Loading etc in android

I want to get clarity on loading of classes, destruction of objects etc in android because I noticed some weird things happening when using Singleton in My Activity. Best I will describe it using code :
My Singleton class
public class FilterCriteria {
private final String TAG=FilterCriteria.class.getSimpleName();
private static FilterCriteria filterCriteria=new FilterCriteria();
private FilterCriteria()
{
}
public static FilterCriteria getInstance()
{
return filterCriteria;
}
private int rentUpperBound,rentLowerBound;
private int bedrooms,baths;
private float distance;
private ObjectStateListener listener;
public void setFilters(float distance,int baths,int bedrooms,int rentLowerBound,int rentUpperBound) {
this.distance = distance;
this.baths=baths;
this.bedrooms=bedrooms;
this.rentLowerBound=rentLowerBound;
this.rentUpperBound=rentUpperBound;
if(listener!=null)
listener.onObjectStateChanged();
}
public void attachListener(ObjectStateListener listener) {
if (this.listener == null) {
this.listener = listener;
Log.v(TAG, "NO LISTENER PRESENT AS EXPECTED");
} else {
Log.v(TAG, "LISTENER PRESENT!!! BUT THE ACTIVITY WAS STARTED JUST NOW.");
}
}
public void destroy()
{
filterCriteria=null;
}
}
The attachListener(ObjectStateListener listener) function is called only once in the activity. So, when I open my activity the first time, I get this log from attachListener function
NO LISTENER PRESENT AS EXPECTED
Now, I close the activity and then reopen it. But now I get this log
"LISTENER PRESENT!!! BUT THE ACTIVITY WAS STARTED JUST NOW."
So, that means the object still lives on even after the activity (and the application) was closed. Is this normal???
So, I tried to destroy the singleton instance using the destroy() function in the onDestroy() function of Activity.
#Override
protected void onDestroy(){
filterCriteria.destroy();//Trying to destroy the singleton
super.onDestroy();
Log.v(TAG,"Destroying activity");
}
But I got NullPointerException on this line filterCriteria.destroy(). So, that means android has already made object null, whereas when I see in debug mode, other members of the Activity are still alive. Why is only this null?
What is happening!???
When you invoke the method attachListener() you are creating a reference to the linked object (even if it is static): this reference will be binded to the activity lifecycle.
On the other hand, filterCriteria will follow the static field Java-like lifecycle (but you can still remove this reference manually).

Keep application state on android

I am having trouble saving the state/singleton of my application.
When the application starts a loading screen (activity) is shown and a singleton is initialized with values from a webservice call (note that network access cannot run on the main thread).
After the singleton is created I open my main activity. Note that values from the singleton are required to build the layout.
Now assume the app goes in the background and is killed there (e.g. because of low memory). My singleton instance is deleted as the app is killed. When I switch back to my app it tries to recreate the main activity. As I mentioned earlier the values from the singleton are required to build the layout, so this leads to a NullPointerException (when I try to access members of the singleton, as it is not there anymore).
Can I somehow tell android to start the first loading activity after the app was killed? It would be great if I could refresh the singleton before the layout is recreated, but this seems to be a problem as network calls can not be on the main thread and therefore not block until the refresh is finished.
I assume that I could save the singleton in all activities onStop and recreate it in the onCreate methods, but this seems a bit too unpredictable and would probably lead to a inconsistent state...
Another way could be to just always finish my activity onStop, but this would lead to losing on which tab the user last and so on, even if the app is not killed, so this is not a good option.
Any ideas on how to solve this?
Why not just use a SharedPreferences instead of a singleton?
Anytime you want to save some global state, commit it to preferences. Anytime you want to read the global state, read it back from preferences.
Then you don't have to concern yourself with application lifecycle at all, as your data will always be preserved regardless of what the phone is doing.
For something like that I used a pseudo singelton object as a Application class. This object will be created on the beginning and will be in the memory. But note that the system will terminate the application if the memory is needed by other applications. However this object is persitent even if all activities are temporally terminated.
To use that you need to declare that in your android manifest like here:
<application android:label="#string/app_name"
android:icon="#drawable/icon"
android:description="#string/desc"
android:name=".MySingeltonClass"
...
Here is a code example:
public abstract class MySingeltonClass extends Application {
// ...
public void informClientOnline() {
clientOnline=true;
Log.v(LOG_TAG, "Client is online!");
}
public void informClientShutdown() {
clientOnline=false;
Log.v(LOG_TAG, "Client is going offline. Waiting for restart...");
Timer t=new Timer("shutdowntimer", false);
t.schedule(new TimerTask() {
#Override
public void run() {
if(!clientOnline) {
Log.v(LOG_TAG, "Client has not restartet! Shutting down framework.");
shutdown();
System.exit(0);
}
}
}, 5000);
}
}
this two functions are called like this:
((MySingeltonClass)getApplicationContext()).informClientOnline();
You could save your Singleton when onSaveInstanceState() in the Activity gets called. All you need to do is to make it implement Parcelable (it's Androids own form of serialization), then you can put it in the outState Bundle in onSaveInstanceState() which will allow you to retrieve it laver in onCreate() or onRestoreInstanceState() in the Activity, whichever you like.
I've included an example for you:
public class TestActivity extends Activity {
private MySingleton singleton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState.containsKey("singleton")) {
singleton = savedInstanceState.getParcelable("singleton");
} else {
singleton = MySingleton.getInstance(5);
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("singleton", singleton);
}
public static class MySingleton implements Parcelable {
private static MySingleton instance;
private int myData;
private MySingleton(int data) {
myData = data;
}
public static MySingleton getInstance(int initdata) {
if(instance == null) {
instance = new MySingleton(initdata);
}
return instance;
}
public static final Parcelable.Creator<MySingleton> CREATOR = new Creator<TestActivity.MySingleton>() {
#Override
public MySingleton[] newArray(int size) {
return new MySingleton[size];
}
#Override
public MySingleton createFromParcel(Parcel source) {
return new MySingleton(source.readInt());
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(myData);
}
}
}

Can I have a multi-process global variable?

I have a real problem using my app that involve 2 processes. One process its executing a Service (p1) and the other the GUI (p2).
I have a class in p2 that implements the use of an object (iThing) that is custom memory managed (and its static). It has to be like this bacause of Android OS implementation of destroying the views whenever he wants.
public class Connections{
public static int iGlobalCounter=0;
public static Object iThing;
public static void start(){
iGlobalCounter++;
Log.d("PROCESS", "UP: "+iGlobalCounter);
if (iGlobalCounter<=1){
//Create the object "iThing"
}
}
public static int stop(){
iGlobalCounter--;
Log.d("PROCESS", "DOWN: "+iGlobalCounter);
if (iGlobalCounter<=0){
//Destroy the object "iThing"
}
}
}
The main GUI (in p2), starts and stops the variable on the onCreate / onDestroy (for all views in my app)
public class MyMainClass extends Activity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Connections.start();
}
#Override
public void onDestroy(){
super.onDestroy();
Connections.stop();
}
}
Finally in p1 I have the service, which also needs the variable, so, it does the same
public class MyMainService extends Service{
#Override
public void onCreate() {
super.onCreate();
Connections.start();
}
#Override
public void onDestroy(){
super.onDestroy();
Connections.stop();
}
}
The problem is that if I use only p2 (GUI), it goes all well, but when I execute the service (in p1), the counter doesn't increment from the last state, but from 0, resulting in destroying the object when leaving the service, not the app.
if do this navigation, I get the following counters:
MyMainClass (1) --> OtherClass (2) --> AnotherClass (3) --> MyMainService (1)
My question is if there is a way of having a multi-process global variable? As it seems that every process takes its own static variables and are not "real static". A solution could be using SharedPreferences to save the state, but not really nice solution, as it hasn't to be saved when leaving the app.
Thanks,
PAU
I think that you should extend Application class and put your globalVariable there.
You can store your global data in shared memory (see MemoryFile).
To synchronize access to the file, I think the best approach is to implement some sort of spinlock using the same memory file.
In and case, I don't know a simply way of doing this.
You have the following options which you can look into for sharing data between different processes,
Message Queue,
Named Pipes,
Memory mapped files
WCF on Named Pipes or MSMQ

Categories

Resources