Binder preventing garbage collection - android

I think I tracked down a memory leak and want to confirm what I think may true about how Android's Binder is implemented. In this case I have a Service and an Activity, each in their own process. I created an AIDL that allows me to pass a Callback object from the Activity to the Service through an ipc method and then have the callback called when the Service is done with the requested task.
For a long time I was wondering: if I pass a new Callback object to the Service and I don't keep a pointer to the Callback object in my Activity why doesn't the garbage collector just go ahead and collect the Callback in my Activity process? Since that doesn't seem to happen, how does the JVM know when to garbage collect the Callback in my Activity.
I think the answer is that the Binder system keeps a pointer to my Callback in the Activity process until the corresponding Callback object in the Service process has its finalize() method called, which then sends a message to the Activity to release the pointer. Is this correct? If not how does it work?
I believe it is and it leads to interesting situation where if the Callback in the Activity is pointing to something very memory intensive it won't be collected until the Callback in the Service is collected. If the Service isn't low on memory it might not collect the Callback for a long time and the Callbacks might just build up in the Activity until there is an OutOfMemoryError in the Activity.

Yury is pretty much correct.
My Service starts a thread that holds the callback and when the thread is done with its work it calls the callback and the thread ends. When the callback is called it may do a tiny bit of work in my Activity and then return at which point I don't have pointers in my Activity process to the callback.
However the callback object in the Activity will continue to be pointed to by Android's binder system until the corresponding callback object in the Service is garbage collected.
If the callback object in the Activity process dominates some other objects that consume a lot of memory then I am wasting memory in my Activity process for no good reason and could even get an OutOfMemoryError. The solution is to create a simple method in my callback class called destory() to null out all the callback's fields and to call that method when I am done with the callback.
If the callback class is a non-static inner class you may want to consider changing it to a static inner class and passing in the parent class in the constructor, this way you can null that out as well in the destory() method.
This brings up an interesting thought, if the parent class of a non-static inner callback class is an Activity and a configuration change happens (such as a screen rotation) after the callback is sent through the binder but before it is called back then the callback will be pointing to an old Activity object when it executes!
Update: I discovered this code inside Binder.java, of course it is disabled but it would have been nice if they mentioned this kind of stuff in the Javadocs.
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Binder> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}

If I understand correctly how Binder works the problem in your case is the following. For each incoming incoming call your Service create a separate thread. When you pass an object to this thread your Binder system creates local copy of your object for the thread. Thus, until your Service method has returned result the thread with the copy of the object continues to work.
To check this just try to see the threads of your Service process (in DDMS).

Related

IntentService and Activity Lifecycle

I followed this tutorial on how to reverse geocode in Android, but I can't understand how this IntentService will deal nicely with the Activity lifecycle. I am following this code sample.
As far as I understand, the Activity will hold an instance of the AddressResultReceiver, named mResultReceiver. When the IntentService get's back the reverse-geocoded address, it will call mReceiver.send(resultCode, bundle) from the mResultReceiver instace, which will then update the UI, inside the Activity.
My question is how will this play along with the Activity lifecycle. Lets assume the Activity started the IntentService and then, by some reason, it was destroyed (like, the user rotated the screen). Later, when the IntentService receives the address back from Google Maps, it will call the mResultReceiver it recevied from the original Activity, that is right now destroyed (or it could have been created again, but still, it will be a new instance anyway).
Since the mResultReceiver is created when the Activity is created, and it isn't saved on any Bundle for later retrieval, when the IntentService completes and calls the original mResultReceiver, what would happen?
Will the mResultReceiver call be automagically forwarded to a new instance of the Activity, like nothing happend and all work as expected?
Will mResultReceiver be Garbage Collected with the destroyed Activity, and the IntentService will throw an exception when it tries to use the destroyed class?
Will mResultReceiver prevent the original Activity from being garbage collected, specially because the IntentServiceis still holding a reference to the mResultReceiver object. And when the IntentService calls mReceiver.send(resultCode, bundle) it will make no difference on the actual Activity on screen, or throw an exception for trying to update already destroyed UI Widgets?
Is this google example flawed? What's the right way to do it then?
I believe the google example is flawed. The sample will result in a memory leak if activity is destroyed. A better approach would be using a local broadcast receiver or an Event Bus or RxJava/RxAndroid
Issue is already reported here

If Asynctask is running will activity will be alive even after finish() method?

On the long run, AsyncTask produces a memory leak : if the AsyncTask lasts
for long, it keeps the activity "alive" whereas Android would like to
get rid of it as it can no longer be displayed. The activity can't be
garbage collected and that's a central mechanism for Android to
preserve resources on the device.
Does that means that if we call finish() method and asynctask is running then also activity will remain "alive"?If not,then what does this means
if the AsyncTask lasts for long, it keeps the activity "alive"?
Activity will be alive unless we call finish() method or we press back button.Does this mean that if asyntask is running then activity will be alive even after that?
If an AsyncTask is declared as a non-static inner class inside an activity, that means it always holds a strong reference to the enclosing outer class where it was created. So, as long as the AsyncTask is running, it holds this strong reference to the activity, which means it will not be garbage collected even after finish() is called and onDestroy() executes. This is a very common problem with AsyncTask that are not properly implemented to prevent this.

Android lifecycle and external threads

I've been staring at my app's log file for a couple of hours and just want to make sure what I see is even possible.
I have an Activity called ActivityA.
It starts up ThreadB.
ThreadB communicates with a server and sends the results back to ActivityA.
I am logging messages in ThreadB when it receives results from the server and sends them back to ActivityA
I am logging messages at the start and end of ActivityA's onDestroy() method.
Here's the weirdness.
ActivityA's onDestroy() method completes. I know it completes because I see the message I print at the end of it in the log.
Then, ThreadB gets some very late input from the server. ThreadB delivers it to ActivityA.
The routine in ActivityA that ThreadB communicates executes. I know it executes because the messages it displays as it runs show in the log file AFTER ActivityA.onDestroy() has completed.
I thought once onDestroy() completed ActivityA would be unreachable. IOW ThreadB could send messages to ActivityA as much as it wanted but ActivityA would not receive these messages because it didn't exist any more.
What in the world am I missing?
You are mixing the Object lifecycle and Activity lifecycle.
If you hold a reference to an Object (the activity in this case) you can execute methods on it.
The Activity lifecycle controls things on a higher abstraction level, determining if it's executing, being presented to the user, etc. It does not control whether methods can be invoked on the Activity object.
You can see this with a simple example:
public class DummyActivity extends Activity {
public int nothing() {
return 1;
}
}
DummyActivity act = new DummyActivity();
act.nothing();
Here I create an Activity and call a method. It does not matter what's the state of the activity, it's only relevant if the object was created and I hold a reference to it.
This might be happening because the ThreadB is a non static inner class of ActivityA, so it has reference to ActivityA, that is why ActivityA is not getting garbage collected. Even if its onDestroy() method is called.
JVM keeps the reference to the currently running thread, and ThreadB in turn has reference to ActivityA. So method of ActivityA gets executed even after onDestroy().
Make the tread as static inner class in your Activity and do not forget to stop your thread in onDestoy(). This would stop the method of ActivityA get executed after onDestroy().
Callback onDestroy does not destroy object Activity, it's still safe to make calls to methods inside some Activity, Fragment or whatever after onDestroy called if you do not touch view elements.
By the way expected behaviour can be reached via Intents and BroadcastListener.
Just implement BroadcastListener with action "my.log.intent" in your activity, register it onStart() and unregister onStop().
Then send broadcast from your logger:
Intent intent = new Intent("my.log.intent");
intent.setExtras(someUsefulData);
LocalBroadcastManager.getInstance().sendBroadcast(intent);
and there you go:
your someUsefulData will be available to Activity regarding activity lifecycle. I.e. when activity visible to user and you are able manipulate contained views.

Passing Timer to a WeakReference

I am not an expert in WeakReferences, as I am getting acquainted by them just now along the way.
I am using Timer with TimerTask in my activity.
It is known that Timers can not be reused whenever you have called cancel() on them, until you create a new instance of it.
So what I am doing is to create a Timer globally in my activity.
Timer timer;
and inside my onResume(), I instantiate it by timer = new Timer();
The reason for this is to avoid the Timer already cancelled exception. Because the onPause() might be called when the mobile gets screenlock or whatever, and I am calling timer.cancel() in my onPause() method.
My question is, when the timer was cancelled, does it automatically becomes prone for Garbage collection as it is totally pointless to use it again AFAIK. If this is the case, how fast does it get collected ?
Is using a WeakReference here comes in handy ? if not, where does it help to use it ?
Regardless of eligibility of Timer to be GC'ed you will not benefit from keeping a WeakReference to Timer instance, because, as you said, it can not be reused anymore after cancel().
WeakReference is useful to keep track of object that you can use, but do not want to keep that object in RAM if no one else uses it (i.e. no one keeps [hard] references to it). Then it will be GC'ed and your WeakReference also becomes useless. (and you do not prevent GC of the object by keeping only WeekReference to it).
Well, hope it is not too vague.
I personally use WeakReferences to have rough idea of possible memory leaks in junit tests.
I could give you another example of usage - instead of having custom inner Handler class defined in Activity class, which has implicit reference to your Activity and could create potential memory leaks, you could define custom Handler class as static nested class and keep WeakReference to your Activity instance as a private member, so you can continue calling your Activity methods from inside custom Handler class.

Avoid Service callback when Activity gets closed and re-opened

I have a LocalService that exposes a Binder with some APIs. I create a Service Listener, just like this:
if (dataServiceListener == null) {
dataServiceListener = new DataServiceListener();
mainActivity.getApplicationContext().bindService
(new Intent(mainActivity, LocalService.class),
dataServiceListener.svcConn, mainActivity.BIND_AUTO_CREATE);
}
After I call the method that the Binder in dataServiceListener exposes, I get the response in the dataServiceListener onResult() method. Up to this point, no kind of issues, everything is working.
Some sort of problem occurs when I close the Activity that is waiting for the Service Listener callback and immediately reopen it. Even though I re-instantiate the dataServiceListener in onCreate(), I get two callbacks instead of one, the old one from the destroyed Activity and the latter (right) one; this way the results mix up on the UI.
Is there a way to tell the Service or the Service Listener that when the activity finishes, the callbacks must be avoided. Or maybe even destroy the ServiceListener objects.
I think this is the issue that Mark L. Murphy (Commonsware) described in "The Busy Coder's Guide to Android Development":
The biggest catch is to make sure that the activity retracts the listeners when it is done.
How can I do this? Is there a way to get rid of the useless listeners when the activity finishes?
Thank you!
I had the same issue. I was working in a remote sevice using AIDL. I got this problem when i am trying do unregister my listeners using the remove method from ArrayList Collection inside a foreach loop, because I was not using asBinder in the comparision. Searching fora solution, I find out the RemoteCallbackList class in Android API. This class does exactly what i needed, and what i think you should do, on a easy way, taken all reponsabilites for the hard work that involves this task.
From the Android API:
To use this class, simply create a single instance along with your service, and call its register(E) and unregister(E) methods as client register and unregister with your service. To call back on to the registered clients, use beginBroadcast(), getBroadcastItem(int), and finishBroadcast().
Broadcast sample:
int i = callbacks.beginBroadcast();
while (i > 0) {
i--;
try {
callbacks.getBroadcastItem(i).somethingHappened();
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
callbacks.finishBroadcast();
The code you show is for binding to a service. You do not show where you are registering a listener with that service. You apparently are, based upon your question and your reference to an onResult() method. Given the nature of your problem, I am going to guess that what you're doing is:
Binding to the service in onCreate()
In onServiceConnected(), you are calling some sort of setListener() method on the Binder
In that case, if we ignore configuration changes, the proper way to unwind matters would be to, in onDestroy(), call some removeListener() method on the Binder, then call unbindService().
Configuration changes, particularly in a pre-fragment world, make this complicated. It's the reason why this sample project (and the accompanying material in the book) is so icky. Binding is twitchy -- if you unbind from the old activity, and nothing else is keeping the service around, the service will shut down before the new activity gets a chance to bind. Binding is also state -- you cannot simply fail to unbind, lest you leak stuff.
So, the recipe becomes:
Bind to the service in onCreate() using the Application Context
In onServiceConnected(), call sort of setListener() method on the Binder
In onRetainNonConfigurationInstance(), make note of the fact that you're undergoing a configuration change, and return some Object that has your Binder, your Listener, and all the rest of your state
In onCreate(), use getLastNonConfigurationInstance() -- if it is null, proceed as normal, but if it is not null, hold onto that Binder and Listener and don't re-bind and re-register the listener
In onDestroy(), if the flag from Step #3 above is false (i.e., we are not undergoing a configuration change), call some removeListener() method on the Binder, then call unbindService().
Using fragments with setRetainInstance(true) can probably simplify this some, though I have not worked through a sample for that yet.
I had this issue too. You need to release all the resources,listeners,threads from the service when it finishes.
Your activity has to register/unregister itself as the listener. You need to use the proper lifecycle callback methods, not onBackPressed(). Register onStart(), unregister onStop(). One way to do it is to make the listener a static member of your service, and provide static register/unregister methods. Then call those from your activity as appropriate.
I finally solved the issue (and no, I haven't been working on it for so long :D).
The callback to the listener was made before the Fragment's onDestroy was called. So the boolean "dontupdate" value was never set to false. Overriding onBackPressed in the main activity solved the problem, as I invoked a destroy() method for each fragment that takes care of setting the boolean value to false.

Categories

Resources