I've encountered a weird issue that has gotten me stumped for a few days now. First some background info:
I have an activity that binds to a third party service (given to me as a library). This service sends asynchronous callbacks to the client (my activity) whenever the client requests an action be performed. The service is never started with startService() and stopService() calls; I perform bind when i need the servcie and unbind when I am done. I never have more than one activity bound to the service at any one time.
My issue:
The user starts my activity (lets call it instance A). I bind to the service in onCreate() and issue a request. I receive a callback from the service and set some local variables accordingly. Once i go through what i need to do, i call finish(). onDestroy() gets called and i then perform un bind service. My assumption at this point is that both the Service instance and my activity should not be able to be referenced any more.
Now, after A has finished, the user starts my activty again (lets call this instance B). I again bind in onCreate and issue a request. However when i receive a callback, i notice that the service has issued a callback to my old "ghost" instance A. I am now using all of instance A's local variables instead of using This new instance B. I even printed out the "this" reference before binding and again after receiving the callback and was able to see that the reference changes from instance B's to instance A's. Execution in my activity continues to proceed, but since my local variables are now set incorrectly to their old state at the end of A's execution, B's execution fails.
So a few questions:
How is it possible that A is still being referenced after
onDestroy() and unbind is called?
Is it likely that this issue could be related to the third party
service and not my implementation? (again, I don't have the
source code to this service, i'm just using it)
Thanks for any insights you all can offer.
-Jason
Turns out it was the the third party service.
Related
Question: What is the best practice for reporting progress/complete from long running task to an Activity? And what to do when the progress/complete report happens while the Activity is in the background/orientation changes?
Real life example:
An Activity makes a network call getting data from a server (this could take 10+ sec).
When this network call is finished, the Activity should be notified and the Activity should show that the network call is finished.
This is easy to implement as long as the app stays open. My problem is, what to do if the network call is finished while the app is in the background (activity will miss any callbacks).
I have been looking at the following ways to do this, but I can not decide what to do:
Service that spawns a thread where the network call is executed. The Service is bound to the Activity. On network call finish, Service callbacks to the Activity. If Activity is in background when the Service makes a callback (therefore the Activity misses the callback), should the Activity poll the Service for saved data?
IntentService that broadcasts the data when network call is finished (what to do if Activity misses this broadcast because it is in background?)
AsyncTask, but this is bad when Activity is in background etc.
How should I approach this problem?
I've solved this problem using HeadlessFragments as its called. This blog post explains in detail how to implement it and the concerns you mentioned are handled by it.
EDIT:
For the questions in the comment:
To your specific question, about callbacks being lost when Activity is in the background, the answer is no. Being in "background" means that Activity is still alive. From the link I posted, the callback, which is the Activity itself, is removed when its detached from the Activity i.e when the Activity is destroyed. So, if your Activity is in the background and not destroyed, it'll still get the callback and do whatever you do in that callback. Although Android can kill Activities that are paused, in which case, your Activity will be destroyed and you won't get the callback. In such case, you can either save the data you get from server in a persistent storage, like SQLite, and prevent making another network call or make the network call when the Activity is created which will ensure that whenever the Activity is created, you'll have data to display (given of course, the call goes through).
The use of Fragment was specifically to handle the configuration change you mentioned in your question. The running task is still being done by the AsyncTask and not by the Fragment. The Fragment only holds a reference to the object. So, I'd argue about it not being a "best practise".
I have an activity that binds to a service. The service provides the functions to interact with an XMPP server.
Then, the activity launches a second activity that needs to access to the same service (for instance to initiate a voice call).
I was thinking to bind the second activity again to the same service, but it seems like an overkill, since when the second activity starts the service should already exist and should be there until the first activity destroys it on purpose (binding again means creating a new connection and waiting for the bind to happen asynchronously before using the service).
At the moment I'm using startActivityForResult() to launch the second activity and then I wait for the result and I access the service from the first activity, but I want to change the logic (the second activity must interact with the service and then the first one takes care of closing the second activity when a signal is received back from the service).
Any suggestion on how can I pass the service object to the second activity?
binding 2 activities to the same service is no overkill. It is actually the proper way of doing what i understand you are trying to do : access xmpp functions provided by a service from 2 different activities.
by binding the second activity, you will not start the service again, as it has already been started. it will connect to the same service as the first activity.
you could, of course, put all the binding to a single place, like a singleton or the Application class.
You could store a reference to it in the Application and then access it from your second activity.
I have a situation which I will try to explain clearly. I have two services, Service A and Service B.
Activity C connects to service A using onBind(). Service A connectes to Service B using a callback object. I want Service A's onbind to not be executed until Service B is connected to Service A. What will be a good way to achieve this?
The right answer is: combine the two services into one.
The next-best answer is: redesign your app such that A is not binding to B, or that C does not care whether A is bound to B.
You can't delay the call from the onBind. If your service's interaction is such that it may not be ready for use until later, you could have a method on the interface returned by onBind that allows a client to get a callback when it is ready. (You could even have this set up where the main interface is just "getRealInterface" that takes a callback, which is given the real interface for your service once you are ready to give it.)
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.
I'm trying to architect a REST-heavy application.
Following one of the models proposed by Virgil in his Google I/O presentation, I've set up my application like so:
My Activity calls upon a "Service Helper" which is a singleton. This singleton instantiates a Service which, upon start, uses a ContentProvider to hit my application's database and spins a thread to hit a web service for any pertinent syncing. The last piece I now need is having the Service callback to the singleton, which calls back to any listening Activities.
This is the part I'm confused about.
Right now, I'm looking at the Android SDK's provided RemoteService ApiDemo. In RemoteServiceBinding.java, an IRemoveService member var is declared called mService. mService is used in various handlers to handle the callbacks.
However, this code will go in my Service Helper (remember this is a singleton). There could be N number of calls to this class to start up Services for database and web service functions. I can't just declare 1 variable called mService, right? The RemoteService declares an ISecondaryService, but I could have N number of pieces of code calling this singleton. It needs to be dynamic.
Ex:
User is on Activity A and wants to show things in a list. The Service Helper singleton is hit, returns a Cursor and starts a Service which is querying a web service for new things. mService is instantiated to handle callbacks for this Service instance.
Before this Service is finished, the user switches to Activity B and does the same thing for the things in that Activity's list. mService is already instantiated for the previous Service instance. What do I do now?
Suppose the connection was slow and the user could do this for 20 Activities before the first one finished getting new things. mService is already instantiated for the previous Service instance. What do I do now?????
What do I do here? I'm at a loss.
It would be really really nice if the Twitter app was open sourced already...
I don't know where you ended up on this, but if you used an IntentService, only one Service would need to be created, and that IntentService would place new calls to Context#startService(Intent i) in a queue.
As the single IntentService handles the queue, they will be processed in the onHandleIntent(Intent i) method of your IntentService. In this method you could to pull extras from the passed Intent to figure out exactly how to handle the requirements set forth by the calling Activity.