I have an Activity calling a Service defined in IDownloaderService.aidl:
public class Downloader extends Activity {
IDownloaderService downloader = null;
// ...
In Downloader.onCreate(Bundle) I tried to bindService
Intent serviceIntent = new Intent(this, DownloaderService.class);
if (bindService(serviceIntent, sc, BIND_AUTO_CREATE)) {
// ...
and within the ServiceConnection object sc I did this
public void onServiceConnected(ComponentName name, IBinder service) {
Log.w("XXX", "onServiceConnected");
downloader = IDownloaderService.Stub.asInterface(service);
// ...
By adding all kinds of Log.xx I found that the code after if(bindService(...)) actually goes BEFORE ServiceConnection.onServiceConnected is being called - that is, when downloader is still null - which gets me into trouble. All the samples in ApiDemos avoid this timing problem by only calling services when triggered by user actions. But what should I do to right use this service after bindService succeeds? How can I wait for ServiceConnection.onServiceConnected being called reliably?
Another question related. Are all the event handlers: Activity.onCreate, any View.onClickListener.onClick, ServiceConnection.onServiceConnected, etc. actually called in the same thread (mentioned in the doc as the "main thread")? Are there interleaves between them, or Android would schedule all events come into being handled one-by-one? Or, When exactly is ServiceConnection.onServiceConnected actually going to be called? Upon completion of Activity.onCreate or sometime when A.oC is still running?
I ended up with something like this:
1) to give the auxiliary stuff some scope, I created an internal class. At least, the ugly internals are separated from the rest of the code. I needed a remote service doing something, therefore the word
Something
in class name2) there are two things necessary to invoke a remote service method: the IBinder and the code to execute. Since we don't know which one becomes known first, we store them:
Each time we write to one of these fileds, we invoke
_startActionIfPossible()
:This, of course, assumes that the Runnable has access to mISomethingService, but this is true for runnables created within the methods of the
RemoteSomethingHelper
class.It is really good that the
ServiceConnection
callbacks are called on the UI thread: if we are going to invoke the service methods from the main thread, we do not need to care about synchronization.ISomethingService
is, of course, defined via AIDL.3) Instead of just passing arguments to methods, we create a Runnable that will invoke the method with these arguments later, when invocation is possible:
4) finally, we get:
context
is a field in my class; in an Activity, you can define it asContext context=this;
I did not need queuing actions; if you do, you can implement it.
You likely will need a result callback in startSomething(); I did, but this is not shown in this code.
*The basic idea is same with @18446744073709551615, but I will share my code as well.
As a answer of main question,
[Original expectation (but not work)]
wait until service connected like below
It won't work because both
bindService()
inonCreate()/onStart()
andonServiceConnected()
is called at same main thread.onServiceConnected()
is never called before wait finishes.[Alternative solution]
Instead of "wait", define own Runnable to be called after Service Connected and execute this runnable after service connected.
Implement custom class of ServiceConnection as follows.
And then use it in the following way (in your Activity class or etc),
It worked for me. But there may be more better way.
I did something similar before, the only different is I was not binding to service, but just starting it.
I would broadcast an intent from the service to notify the caller/activity about it is started.
I figured out that these workarounds are only worth the effort and the wait only if your bound services are running in a different process than your application's main process.
For accessing data and methods in the same process (or application), I ended up implementing singleton classes. If the classes need a context for some methods, I leak the application context to the singleton classes. There is, of course, a bad consequence of it as it breaks the "instant run". But that is an overall better compromise, I think.
You don't. You exit out of
onCreate()
(or wherever you are binding) and you put you "needs the connection established" code inonServiceConnected()
.Yes.
Your bind request probably is not even going to start until after you leave
onCreate()
. Hence,onServiceConnected()
will called sometime after you leaveonCreate()
.I had the same problem. I didn't want to put my bound service dependent code in
onServiceConnected
, though, because I wanted to bind/unbind withonStart
andonStop,
but I didn't want the code to run again every time the activity came back to the front. I only wanted it to run when the activity was first created.I finally got over my
onStart()
tunnel vision and used a Boolean to indicate whether this was the firstonServiceConnected
run or not. That way, I can unbindService inonStop
and bindService again inonStart
without running all the start up stuff each time.