I'm trying to create a service that is primarily run in the foreground and clients can bind to it only if it is already in the started state. However, every potential solution I can come up with is riddled with race conditions.
The most promiment example of this:
final ServiceConnection serviceConnection = // create instance...
void startAndBindToService() {
final Intent serviceIntent = new Intent(this, MyService.class);
startService(serviceIntent);
// this *sometimes* doesn't bind because the service isn't started yet
bindService(serviceIntent, serviceConnection, 0);
}
Testing on an emulator, the above works consistently on a local service but not when the service is in another process.
I've also looked into having the service starting itself when its onBind method is called:
#Override
public void IBinder onBind(Intent intent) {
startService(new Intent(this, this.getClass()));
return binder;
}
But this never guarantees the service will be in the started state before the client unbinds.
And for the ones that think they are clever and ask why I don't use BIND_AUTO_CREATE, that doesn't solve the problem, and in fact it only makes it worse. BIND_AUTO_CREATE only creates the service, it doesn't put it in the started state, but it does allow for the service to end being created twice if a race condition does occur.
Related
I have a class that extends Application, it gets a generated ID from a bound service.
public MyApp extends Application
{
public void onCreate()
{
super.onCreate();
Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
String id = myService.getID();
}
}
My problem is, id results to null. How can I get the Id from the service to my application class? I assume, the compiler creates the application before starting the service.
You need to handle the onServiveConnected event on serviceConnection object.
Straight from the docs:
A client can bind to the service by calling bindService(). When it
does, it must provide an implementation of ServiceConnection, which
monitors the connection with the service. The bindService() method
returns immediately without a value, but when the Android system
creates the connection between the client and service, it calls
onServiceConnected() on the ServiceConnection, to deliver the IBinder
that the client can use to communicate with the service.\
I have a bound service that in turn binds multiple services using the same ServiceConnection object. In the onServiceConnected I save the ComponentName and the Binder of each service inside a Map, so that I can use them individually. At a certain point I'd like to unbind some of these services separately. Is there a way to do this in Android?
The only way I was able to find out to unbind a service is to use unbindService(ServiceConnection), but I don't think that I can unbind a specific service using that.
Why this seems to be not supported? Are there any downsides?
Despite the time elapsed by the library and for offering support to lower versions in Android, it works correctly, I am also using services nested in a main one and the connections work or go through the main one. They are multiple Good note that when you start a connection via intent you must close it In the same way, it creates problems in the management of processes when the service is reused by other processes within the application. When closing the connection and instantiating a new one, it creates a small error in the process that was not terminated correctly.
Just to mention how it is currently done! happy code!
here is sample code to bind and unbind service
i = new Intent(this, YourService.class)
protected ServiceConnection mServerConn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.d(LOG_TAG, "onServiceConnected");
}
#Override
public void onServiceDisconnected(ComponentName name) {
Log.d(LOG_TAG, "onServiceDisconnected");
}
}
public void start() {
mContext.bindService(i, mServerConn, Context.BIND_AUTO_CREATE);
mContext.startService(i);
}
public void stop() {
mContext.stopService(new Intent(mContext, ServiceRemote.class));
mContext.unbindService(mServerConn);
}
First question here, but I've been around for a while.
What do I have:
I'm building an Android app which plays audio streams and online playlists. Everything is working fine now, but I'm having issues in communicating with my service.
The music is playing in a Service, started with startForeground, so it doesn't gets killed.
I need to communicate from my activity with the service, for getting the track name, image, and a couple of things more.
Whats my issue:
I think I need to start my service with bindService (instead of my current startService) so the activity can talk to it.
However, when I do that, my service gets killed after closing the Activity.
How can I get both? Binding and foreground service?
Thanks!
No. bindService will not start a service . It will just bind to the Service with a service connection, so that you will have the instance of the service to access/control it.
As per your requirement I hope you will have the instance of MediaPlayer in service . You can also start the service from Activity and then bind it. If the service is already running onStartCommand() will be called, and you can check if MediaPlayer instance is not null then simply return START_STICKY.
Change you Activity like this..
public class MainActivity extends ActionBarActivity {
CustomService customService = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// start the service, even if already running no problem.
startService(new Intent(this, CustomService.class));
// bind to the service.
bindService(new Intent(this,
CustomService.class), mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
customService = ((CustomService.LocalBinder) iBinder).getInstance();
// now you have the instance of service.
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
customService = null;
}
};
#Override
protected void onDestroy() {
super.onDestroy();
if (customService != null) {
// Detach the service connection.
unbindService(mConnection);
}
}
}
I have similar application with MediaPlayer service. let me know if this approach doesn't help you.
Quoting Android documentation:
A bound service is destroyed once all clients unbind, unless the service was also started
And about the difference between started and bound just take a look to https://developer.android.com/guide/components/services.html
So, you have to create the Service using startService and then bindService, like #Libin does in his/her example. Then, the service will run until you use stopService or stopSelf or until Android decides that it needs resources and kills you.
Today, working with the Service for Android, has faced some "illogical" or "not correctly", in my opinion, the work method bindService. The essence of the confusion that I created in the application service that is within you, with the ExecutorService makes a request. When I went to the application, the service remained alive - the queries are executed in separate threads with a certain cyclical (logs confirm this). In the method onStart() I have written code that, by all manuals and tutorials should give me access to the service, that I have run before, with this application. But all our advise it seemed evident. I expected that calling bindService() -> I get a connection to a running service. But no, instead, at the first attempt the connection is not happening - I do not understand why. I added code that would run the service itself, if it has not been done before. So this part of the code is activated and again I try to connect to just running the service. And yes, the connection is successful, but - connection nourish my service I was expecting to get from the first connection attempt. And judging from the logs of my attempt to re-create the service does not lead to its creation. All this follows from the log. And in this regard, I wonder - why the first attempt to connect it does not happen? Or am I doing wrong?
fragment code in Activity
...
private ServiceConnection serviceConnection = new ServiceConnection(){
public void onServiceConnected(ComponentName name, IBinder service) {
flagServiceConnection = true;
Log.d("StartActivity/serviceConnection", "serviceConnection/onServiceConnected() -> connected");
exService = ((ExService.ExBinder) service).getService();
exService.setFlagBroadcast(true);
exService.getAll();
}
public void onServiceDisconnected(ComponentName name) {
flagServiceConnection = false;
Log.d("StartActivity/serviceConnection", "serviceConnection/onServiceDisconnected() -> disconnected");
}
};
...
public void onStart(){
super.onStart();
bindService(new Intent(this.getApplicationContext(), ExService.class), serviceConnection, 0);
if(!flagServiceConnection){
Log.d("StartActivity", "onStart() -> start service");
this.startService(new Intent(this.getApplicationContext(), ExService.class));
bindService(new Intent(this.getApplicationContext(), ExService.class), serviceConnection, 0);
}
}
Log
D/StartActivity(5922): onCreate()
D/StartActivity(5922): onStart() -> start service
D/StartActivity/serviceConnection(5922): erviceConnection/onServiceConnected() -> connected
D/-(5922): pront.android.exservice.ExService$Monitor#4056b4c8
D/-(5922): pront.android.exservice.ExService$Monitor#405480e0
D/-(5922): pront.android.exservice.ExService$Monitor#4054ee18
D/ExService(5922): onRebind()
D/ExService(5922): onStartCommand() -> service start
Your first connection attempt works, but your flagServiceConnection check doesn't, so you always try to connect one more time, and here's why.
When you call bindService() method, you are not connected to the service immediately, so your flagServiceConnection is not set yet when you try to check it.
I assume the purpose of your check is to start service before binding if it is not started yet. To achieve this, you need to call bindService() with BIND_AUTO_CREATE flag:
#Override
public void onStart(){
super.onStart();
bindService(new Intent(this.getApplicationContext(), ExService.class), serviceConnection, BIND_AUTO_CREATE);
//that's it, if service is not started, it will be started automatically
//no need for additional checks
}
}
I have been looking for some way to get the ServiceConnection when I start my Service using startService(...).
I haven't found a way, so I have been searching a bit and found this:
Does each Activity need to Bind to a Service & What happens when it was created with startService()
There, Commonsware says that it doesn't matter if I call bindService after the startService call.
So I thought that I first run startService(...) and then directly after do a bindService(...) (so that onServiceConnected is called). But then the Service.onCreate is executed twice. Probably because startService isn't "finished" yet...?
Question is: How do I get a reference to my Service (the IBinder), ie. how do I get the onServiceConnected to fire if I start my Service with startService?
--- EDIT ---
I still do want to know any answers and ideas you might have. I made a "hack" to get around this:
I simply made a static reference (in SRef.java I have public static IBinder myBinder = null), and in my Service.onCreate I simple do
SRef.myBinder = myBinder;
This doesn't seem right to me, so any other ideas on how it is supposed to work would be appreciated.
I use the exact same technique (a samba client service), onCreate is never called twice for me and I get the binder (by connection callback) as I would expect. A new start of activity doesn't fire a onCreate either because previous startService performed the startup of the service already.
Here is my code (might be trivial, but maybe it helps):
Activity (onCreate):
startService(new Intent(this, SambaService.class));
bindService(new Intent(this, SambaService.class), sambaServiceConnection,
Context.BIND_AUTO_CREATE);
Service:
private ServiceBinder mServiceBinder = new ServiceBinder();
public class ServiceBinder extends Binder {
public SambaService getService() {
return SambaService.this;
}
}
public IBinder onBind(Intent intent) {
return mServiceBinder;
}