I've got a service that's bound to from a couple of activities, each using a ServiceConnection.
Each activity needs to check before calling the service whether the service is already in use. So in the service I have a function (let's say getCurrentId() ) which returns details of what the service is currently doing.
Then in the client activity, the service connection is set up:
private MyService mService = null;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
MyService.MyBinder myBinder = (MyService.MyBinder) binder;
mService = myBinder.getService();
activeId = mService.getCurrentId();
log.i(TAG, "Service bound" );
}
public void onServiceDisconnected(ComponentName className) {
log.i(TAG, "Service has been killed");
mService = null;
}
};
A button toggles binding to the service:
activity.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
and unbinding:
activity.unbindService(mConnection);
I'm not calling startService() at all.
Before I bind to the service, I check if it's already active and what it's doing:
if (mService == null)
activeId = -1;
else
activeId = mService.getCurrentId();
The problem is, if an activity binds to and then unbinds from the service, the service onDestroy() method is called (I've logging in it to confirm this), which is fine.
BUT this doesn't trigger onServiceDisconnected().
So mService is never set to null, and when I get to that if statement, it happily carries on and calls getCurrentId(), which returns whatever the previous details were.
I gather that onServiceDisconnected() is only supposed to be called when the thread the service is running in is unexpectedly killed, so it's correct that it's not called when the service is destroyed due to the last activity using it unbinding.
As far as I can tell, the service isn't being reinstantiated, I've got logging throughout it.
Which gives me two questions:
Is there an alternative callback function or some way where a ServiceConnection is notified that its service has been destroyed by unbinding?
If the service has been destroyed, then how can I still call its functions? Or is something else going on - is the ServiceConnection or the Binder somehow returning the value without actually calling the service?
onServiceDisconnected() is only called
when a connection to the Service has been lost. This typically happens
when the process hosting the service has crashed or been killed.
Quoted from the Android docs. This seems to be a very rare case, and will not be called when simply unbinding normally from a service.
To keep my connections with a service sane, I would suggest you bind to the service in the Activities onResume method and unbind from it in the onPause method.
Related
I have a background Service that need to be running even if the application gets killed by Android. This is currently working perfectly.
My problem is that when I restart the application (with the background service still running), I want my Activity to bind to the service to have access to some of its methods. When I try to bind with a ServiceConnection, the onServiceConnected is never called.
final private ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(TAG, "onServiceConnected"); //this is never called
MyBackgroundService.ServiceBinder binder = (MyBackgroundService.ServiceBinder) service;
backgroundService = binder.getService();
}
public void onServiceDisconnected(ComponentName className) {
Log.d(TAG, "onServiceDisconnected");
backgroundService = null;
}
};
private void bindBackgroundService(){
this.bindService(new Intent(this, MyBackgroundService.class), serviceConnection, Context.BIND_AUTO_CREATE);
}
Am I doing this the wrong way? Is it better to stop the Service and restart it?
Since the class that binded the background service is a singleton and my alarm broadcast receiver that is making sure the background service is always running instantiates this singleton, I had access to this singleton and I was trying to bind to the service that was already binded.
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 am using an android service as explained here. I am calling doBindService() in onCreate() and trying to call one of mBoundservice's methods in onResume(). This is where I run into an issue. At this point, mBoundService is still null, presumably because mConnection.onServiceConnected() hasn't yet been called.
Is there some way to be able to call methods of mBoundService in onResume(), or is there no way around it's being null at that point?
It hasn't been clear stated in the official dev guide that bindService() is actually an asynchronous call:
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.
There is a lag (although instantaneous but still a lag) after calling bindService() and before system prepare/instantiate a usable service instance (not NULL) and hand it back in ServiceConnection.onServiceConnected() callback. the time interval between onCreate() and onResume() is too short to overcome the lag (in case if activity is opened first time).
Suppose you want to call mBoundservice.foo() in onResume(), a common workaround is call it in onServiceConnected() callback when activity is first created, and set a boolean state, and in onResume() method, only call it iff the state is set, to conditional control the code execution i.e. calling mBoundservice.foo() based on different Activity lifecycle:
LocalService mBoundservice = null;
boolean mBound = false;
... ...
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalBinder) service;
mBoundservice = binder.getService();
mBound = true;
// when activity is first created:
mBoundservice.foo();
}
... ...
};
... ...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// call bindService here:
doBindService();
}
#Override
protected void onResume() {
super.onResume();
// when activity is resumed:
// mBound will not be ready if Activity is first created, in this case use onServiceConnected() callback perform service call.
if (mBound) // <- or simply check if (mBoundservice != null)
mBoundservice.foo();
}
... ...
Hope this helps.
I'm not good in English, but I would try to explain my problem in good way.
So, the problem is:
1) I have a local service
2) I start it and then bound to it.
3) Problem appears when I am about to close that service. onServiceDisconnected method from my implementation of class ServiceConnection is never called. If I close it manually (from settings), or by unbindService, or by stopService, or by combination of unbindService and stopService - onServiceDisconnected still doesn't to be called.
What am I doing wrong?
Short code is below:
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 is defined upper in code, I think it is not necessary to explain what is it
mContext.bindService(i, mServerConn, Context.BIND_AUTO_CREATE);
mContext.startService(i);
}
public void stop() {
mContext.stopService(new Intent(mContext, ServiceRemote.class));
mContext.unbindService(mServerConn);
}
I'm testing this code under emulator of Android 2.2
onServiceDisconnected is only called in extreme situations (crashed / killed).
which is very unlikely to happen for a local service since all your application components normally run in the same process... meaning, unless you intentionnaly unbind or destroy the service, it should remain connected, or die with the component using it.
Android developer documentation says...
public abstract void onServiceDisconnected (ComponentName name)
Called when a connection to the Service has been lost. This typically happens when the process hosting the service has crashed or been killed. This does not remove the ServiceConnection itself -- this binding to the service will remain active, and you will receive a call to onServiceConnected(ComponentName, IBinder) when the Service is next running.
For more: https://developer.android.com/reference/android/content/ServiceConnection#onServiceDisconnected(android.content.ComponentName)
I used Context.BIND_AUTO_CREATE in bindService. I have fetch same issue after apply its working perfect for me.
bindService(new Intent(this,XMPPService.class),mConnection, Context.BIND_AUTO_CREATE);