Does this Activity - Service messaging pattern cause a memory leak? - android

I have a Worker Service, and a bunch of Activities. Every Activity has a Handler. The Activites can start jobs by sending Intents to the Service with the startService method. These jobs get into a queue, and are processed sequentially.
To report back the results of the finished jobs, I supply the proper Activity's Handler wrapped in a Messenger object with the Intent that describes the job. The Service sends back some message for the calling Activity through this. This works just fine.
But the supplied Handlers seems to remain in memory, even after the corresponding Activites got destroyed, continuing to handle the completion messages.
How can this be? Shouldn't the handlers be destroyed with the Activity they're part of?
Does this cause a memory leak?

Since the Service has a reference to the Itnents which still have a reference to the Activities Handler then the Handler isn't going to become available for Garbage Collection.
Have you seen this video from Google IO? It's about building apps that use RESTful web services so isn't directly relevant but the approach of delegating the responsibility for responding to long running events will partially map to your problem.
If you don't want to change things around too much then you need a way in your Activities onClose or onDestroy to notify the Service (and through it the Intent) that the long running task it is in charge of isn't needed anymore. It removes it's reference to the Handler and so the GC can get rid of it.
That or centralise the management (as in the video) of running tasks and responding to their lifecycle in a ServiceHelper class. That class can publish events as things happen and the activities can listen for those events.
This has the benefit of your code being in one place so you don't have to change every activity when you want to change how you handle the service.

Related

How to manage stopping a Service with a long-running onDestroy?

I have an android Service class which has a long running onDestroy. I need to prevent this as it can cause a hang when there are activities running.
It seems some people are happy starting a thread/AsyncTask in the onDestroy method to hold the long running code, though I'm concerned that the threads may be killed. Another solution may be to use startService instead of stopService with an intent that tells the service to start a shutdown thread which calls stopSelf at the end.
Are any of these solutions sensible, or is there another way?
A shutdown Intent is a reasonable way to go here.
Starting another Thread in onDestroy is a bad idea though. It might be called or not called when you don't expect or want it.
Edit: To persist important information neither of these ways is a good idea.
You cannot assure that these methods actually get run before your process is killed. For non-important data you could of course go these ways, but you'd better persist your data as soon as you get it, or at least within a fixed interval (if you have a continous data input).
From the official Documentation:
Note: do not count on this method being called as a place for saving data! For example, if an activity is editing data in a content provider, those edits should be committed in either onPause() or onSaveInstanceState(Bundle), not here.
This method is usually implemented to free resources like threads that are associated with an activity, so that a destroyed activity does not leave such things around while the rest of its application is still running. There are situations where the system will simply kill the activity's hosting process without calling this method (or any others) in it, so it should not be used to do things that are intended to remain around after the process goes away.
And here the Documentation specifcally for services:
Called by the system to notify a Service that it is no longer used and is being removed. The service should clean up any resources it holds (threads, registered receivers, etc) at this point. Upon return, there will be no more calls in to this Service object and it is effectively dead.
(I've included the Activities documentation, because it is more precise)
You should be aware that there is no absolute guarantee for onDestroy to be executed.
It seems some people are happy starting a thread/AsyncTask in the onDestroy method to hold the long running code, though I'm concerned that the threads may be killed.
I would assume that you're trying to either free some resources or send some sort of message to server.
In case of resources there is no need worry - if you'll start new thread it will be killed only together with hosting process (your app). If that would happen - it's ok, system will release resources for you.
In case of server message - that is a bit more complicated. I like your idea with sending command to a Service instead of calling stopService. Other option would be to start another tear-down Service from your onDestroy which will perform long running operation and shut down itself.

Why is my Service stopping?

I wonder what conditions must be met so Android stops a Service, besides the obvious, running low on memory.
See, I run a bootstrapping Service, holding a local BroadcastReceiver. The service itself contains a state machine and so I transition from one bootstrapping state to another. Each states transition function starts either an AsyncTask or an IntentService and once it's done, it dispatches an Intent back to Service to let it know that the task was executed successfully. However, during that, the service just stops, and thus the intent is being dispatched into nothing, which is frustrating.
I don't want to use a Foreground Service and I don't want to trick around just to keep my service alive. I also don't want to rely on onHandleIntent() of an IntentService, since the IntentService looses all its states, meaning all members get nulled once ran through, so when stateA is finished and stateB should be kicked off, the whole statemachine is null, all previous states are gone, etc....
I commit the Services context to each IntentService/AsnycTask and I thought, as long as they hold a reference of the service context, the Service wont stop. Sadly, this is not the case...
Do I have to invoke a method on the context while doInBackground() an AsyncTask, just to keep the Service alive or something?
Beside system running out of memory, you can get your process killed if you exceed certain amount of memory with your service(check limitations).
Like with service processes, foreground processes can extend your service's lifespan, but you have no guarantees either.
I think your best shot is making a logic that will go with services in START_STICKY mode(link).
I wonder what conditions must be met so Android stops a Service, besides the obvious, running low on memory.
A service can be stopped by the user at any time for any reason, either by directly stopping the individual service, or (more likely) by terminating the whole process.
Android will stop a service individually if it crashes. More commonly, it terminates the entire process to reclaim that process' memory for other uses.
I run a bootstrapping Service, holding a local BroadcastReceiver
I doubt anyone but you knows what "bootstrapping" means in this context.
Each states transition function starts either an AsyncTask or an IntentService and once it's done, it dispatches an Intent back to Service to let it know that the task was executed successfully.
Using an AsyncTask from a Service is frequently pointless. You do not want to do stuff on the main application thread in a Service, and the point behind an AsyncTask is to do some work on the main application thread (e.g., onPostExecute()). Use a Thread.
Using an IntentService from a Service is pointless and wasteful. You already have a Service -- it's doing the "bootstrapping". You do not need another Service. Use a Thread.
However, during that, the service just stops, and thus the intent is being dispatched into nothing, which is frustrating.
More likely, the whole process is gone.
I commit the Services context to each IntentService/AsnycTask
I am not completely clear what "commit" means here, but if you are passing a Context (Service) to another Context (IntentService), that is a serious code smell.
as long as they hold a reference of the service context, the service wont stop
No. Moreover, none of that stops Android from terminating your process.
Do I have to invoke a method on the context while doInBackground() an AsyncTask, just to keep the Service alive or something?
No, you need to keep your state machine persistent (a.k.a., a file), so that if your process terminates for whatever reason, when you next run again, you can pick up from where you left off.

AsyncQueryHandler vs IntentService?

Looking at the source code for the stock Calendar app, Google uses an IntentService to do the content provider operations for the database. Is there an advantage to using an IntentService rather than a AsyncQueryHandler?
I thought that since a Handler is tied to the UI, if the activity gets paused or stopped, the Handler would also get paused. However, this doesn't seem to be the case: I created a simple content provider and AsyncQueryHandler that do nothing but run through a long for loop. When I launch other apps or kill the activity, the for loop still runs.
So is the advantage of using an IntentService for asynchronous CRUD operations that the service (being a service) has less of a chance of getting killed?
Update: Part of my confusion is how a handler relates to an activity's lifecycle. From my experiment, it seems that it's independent.
Also, for those not familiar with the stock Calendar app source code, the way it works is that to do a CRUD, it adds the operation to a queue along with a reference to a handler. Then it starts the intent service which pops the queue and does the CRUD. When it's finished, it invokes the handler via Message.sendToTarget().
So what does that extra complexity buy us?
I am stuck with the same confusion. What I found is the IntentService will get killed with the activity. This also means that it will get killed on Orientation change. While an AsyncQueryHandler will keep on running(as you found out). In my use case I am updating my UI first without waiting for the database to update. So I think I am gonna opt for AsyncQueryHandler.
I don't know much about Intent Service(never used it) but as it is sending messages It might be useful when you need something from database to show on the UI thread..so that you know when the database was updated.

Calling a lengthy Service method from an Activity - Best Practice

I'm developing an app with a service that forwards calls to a web-service, and a few activities that place those calls. The activities need to process the results of those calls. For example, I have a writeComment method on the service, that accesses the web-service and returns some information about the newly written comment.
Right now I let the Activity take care of all the threading. The Activity binds the service, and then uses an AsyncTask that calls the bound service's writeComment method.
All works well as long as the Activity isn't stopped while the AsyncTask is running. If it does (easily happens when flipping the phone), the AsyncTask dies a violent death when trying to update the UI in onPostExecute. I'm not entirely sure how to fix this - I do need to let the user know the server has been updated.
If I go the other way around, and register a callback with the Service, I'm still a bit stump, because I need to notify the Service the Activity has changed - I need to tell it not to notify me in the first Activity's onDestory, and reregister in the second Activity's onCreate. And I need to handle the case where the asynchronous task completes after onDestroy and before onCreate.
What is considered Best Practice in this case?
Thanks,
Itay.
My intuition tells me to let the service handle the threading. Services are far less transient (although still transient to some degree) than activities and therefore you'll have less issues of threads trying to interact with a Context (be it an Activity or a Service) that's no longer there. Have you looked at the IntentService class? It handles a lot of the threading for you.
In my app, I have a long-running service and Activities that need to render data in the service. The service also pings the Activities when there is a change but the Activity can also query the service. The way I approached this was two-fold.
Firstly, I bind my activity to the Service in order to send messages from Activity to service.
Secondly, the Service sends notifications with Broadcasts and the Activity listens for those broadcasts. I set that up in the Activity onResume and tear it down in the onPause. I think this is the part that you're missing.

Activity as a listener and memory leaks

I have the following framework for my application:
1. a Network thread that runs in the background (a queue) for issuing request and get async responses. The thread is started and stopped in the Application Object so it's leaving through out the whole application.
2. a DataManager which is also a member of Application and has different DataManagers for the data types i retrieve from the network. the data manager itself is the listener for the responses from the network so it's safe until the application itself dies.
3. this is the problematic part. Some of my Adapters and part of my Activities are DataListeners for my DataManagers, that means that the data manager keeps a reference to them.
When a phone call or some other phone event occurs i've noticed that the activity is usually in paused and not destroyed and so receives my events, which is ok. the problem starts when landscape\portrait is changed. since i keep a referenced to the activity in an Application bound object, the activity can't be destroyed on one hand, BUT the event is still getting to the listener, only the wrong one...
Basically i can fix that issue by removing the listener in onDestroy and retaining configuration boolean to tell me that request was allready issues and i just need to put a listener and try to retrieve the data from the data manager.
However :-) i was wondering how android handles this cases usually, if for example this was a Service running. or if the Service is a local Service that used Bound and passed on the Activity as a Listener to the network Event, the same things happen, untill the listener is not removed the Activity is leaked and lives on, but without it, no way to get callbacks from the network...
an Intent requires serilaztion and deserilazation of data which can be heavy (Bitmaps for example?)
And anywa, asuming i send an intent on each respose i get, how do i get the intent to the Activity (i know of getIntent, but if i get another one , not related, do i get it as an 'event' ?)
From what I gather it's customary on Android to remove yourself from listener lists when the activity is destroyed. It's kinda error-prone, but I think it's the generally accepted way to do it.
You could imagine your service accepts only one listener, which may or may not fit your case, and when the activity restarts its registering with the DataManager would overwrite the old activity which would in turn be garbage collected. The drawback is, you don't free the activity memory if it is destroyed but the service lives on, so it's probably better to just remove the activity from listeners.
Android development is rather different from other platforms (e.g. BlackBerry). I'm not able to give you a quick silver bullet solution, however here are my thoughts on this:
Some of my Adapters and part of my
Activities are DataListeners for my
DataManagers, that means that the data
manager keeps a reference to them.
OS kills Activities according to their lifecycle. So you should avoid keeping a handle to an Activity in another object which is supposed to live after the Activity is destroyed by OS. Otherwise you'll get memory leak.
Also keep in mind Application sublass instance does not always live for the whole application session (a session from a user perspective). If your app goes in the background, for example, due to an incoming phone call, then your entire process can be killed. See details here. As soon as you Application sublass contains some state which is not persisted if process is killed you may mistakenly expect your handles to point to some non-null entities. However after going to foreground (and process restore) those may just be nulls because a new instance of Application sublass has been created by OS.
Ok, so let me describe the problem and the solution i found in more details.
The problem:
I have a Service\Network Thread that needs to notify Activities that sent requests through it that either request or Error has arrived in an Async way. Using Listener Pattern requires me to set listener before or when i send a request like so:
mNetService.setRequest(request, this);
where this is Activity that implements my listener Interface.
But doing it this way requires me to remove the listener from the service in onDestroy and returning the listener, if i ever sent a request back in onCreate\onResume, but the response can also arrive exactly when the activity is not listening (landscape\portrait event) which requires me to keep the Error\Response in the service until some1 picks it up and resets it.
The solution i found:
using Broadcasts and BroadcastReciever.
this is only part of the solution but it let you have a listener to broadcasts (that can be specific for a certain class type meaning Activity) and action.
Since all of my Activities inherit a base Activity class i've made they all have a BroadcaseReciever inner class that listens on certain action in it's filter.
is i enable the listening in the C'tor of my Activity the listener will be registered in onResume and deregister in onPause.
If the listener gets onRecieved event it will call a method in the Activity (which i can override in my specific activty) and pass it the Intent i got which can contain all the data from the response.
The only missing part is what happens if the Activity dies for a second and only then the broadcast arrives ? ah, that's a problem, so android intorduces Sticky Broadcasts that stays there untill you remove them with removeStickyBroadcast(Intent), so when is ent broadcast from my service i send Sticky broadcast, when the Activity gets my Broadcast it removes it so it wont stay around and mislead the activity about new response that arrived.
The only problem with it is if i send a request, don't wait for the response and goes to the next Activity right away, in this case when i'll go back to that Activity it will think it got the response. Didn't find a proper solution to that just yet. But it's better then my previous solution.

Categories

Resources