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);
}
Related
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
}
}
My activity starts a Service, and when I close my app, the service continues to run.
OK, that's right. But when I open my application again, in the activity, I need to know the value of a public variable defined on the running Service(class) that I've started previously.
How can I do that?
Thanks
If you are binding your Activity to the Service, you should have an implementation of the Binder interface in your Service, e.g.
public class ServiceBinder extends Binder {
public MyService getService() {
return MyService.this;
}
}
In your Activity, create a new ServiceConnection class which will be used to give you access to your Service:
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mMyService = ((MyService.ServiceBinder)service).getService();
}
public void onServiceDisconnected(ComponentName className) {
mMyService = null;
}
};
Here the member variable mMyService will give you access to all public members of your Service class.
To create the connection, implement doBindService and doUnbindService in your Activity:
void doBindService() {
bindService(new Intent(this, MyService.class), mConnection, Context.BIND_AUTO_CREATE);
}
void doUnbindService() {
// Detach our existing connection.
unbindService(mConnection);
}
Hope this helps!
If you don't call unbindService, your activity still have connection to service and you can simply check the variable through the service's method.
You could use messenger.
As per android website
A messenger is reference to a Handler, which others can use to send messages to it. This allows for the implementation of message-based communication across processes, by creating a Messenger pointing to a Handler in one process, and handing that Messenger to another process.
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);
I'm trying to remote control a live wallpaper from a widget. They're in the same APK, but obviously different processes. Calling an "activity" of the live wallpaper is of little use to me since it is a different process. The widget has simple buttons that, when pressed,
So what (I think) I need is IPC and AIDL.
First I created the AIDL on the wallpaper side, which worked fine. It has three methods with no extra parameters. But when I added the clientside to the widget, I got an error telling me that I cannot bind to that remote interface because the widget is already a BroadcastListener. I tried getting button handling in without needing the Widget to be a BroadcastListener, but that seems to be impossible.
Well no problem, right? I just created a service within the widget that binds to the remote interface, because while the widget is a BroadcastListener, the service is not, and everything should be fine.
Or so I thought.
Well, I'm getting the widget's buttons to trigger the widget service. Binding to the remote service yields me the following warning:
Unable to start service Intent (act=com.blabla.IRemoteService): not found.
I am using getApplicationContext() within the service of the widget to bind to the remote stuff. I do have the widget service in the manifest, but I don't have the remote service in there. When I do put it in there, I get a nonspecific InstantiationException.
In the Widget's Service onStart() I am doing this:
getApplicationContext().bindService(new Intent(IWallpaperRemote.class.getName()),
mConnection, Context.BIND_AUTO_CREATE);
I also have...
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
mService = IWallpaperRemote.Stub.asInterface(service);
isBound = true;
Log.i("WidgetServiceConnection", "binding to service succeeded");
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
isBound = false;
Log.i("WidgetServiceConnection", "binding to service lost!");
}
};
My question is this: Has anyone ever successfully done a remote call from a widget into another application? Considering I am talking about a live wallpaper here, and the fact that I'm not interested in calling an activity within the widget process but cause function calls within the live wallpaper, what options do I have other than IPC, if any?
And if IPC is the way to go here, what am I doing wrong?
I found the answer to my own question. To make things easier for others, here's the solution:
When doing a remote service, one has to write the AIDL which will be compiled into a sort of stub interface, the implementation of that interface (i.e. the code that is executed when someone calls the remote methods), and a class that extends "Service" which returns the implementation class in the onBind() method. (A normal local service would return null in that method)
Now what I had not understood is that you MUST have a service definition in the manifest - WITH INTENT FILTER!
Let's say your AIDL is called IRemoteService.aidl, then you have a class called RemoteService which looks like this:
public class RemoteService extends Service {
public IBinder onBind(Intent intent) {
Log.i("RemoteService", "onBind() called");
return new RemoteServiceImpl();
}
/**
* The IRemoteInterface is defined through IDL
*/
public class RemoteServiceImpl extends IRemoteService.Stub {
public void remoteDetonateBirthdayCake() throws RemoteException {
//your code here
}
};
}
In your android manifest, you want this:
<service android:name="RemoteService">
<intent-filter>
<action android:name="com.sofurry.favorites.IRemoteService"></action>
</intent-filter>
</service>
Note the service name: It's "RemoteService", not "IRemoteService" or even "RemoteServiceImpl". You need the name of the class that extends "Service", whose onBind method we overrode.
To complete the thing, here's the code on the client side -and yes this code also works from within another service, for example one you started from your widget ;)
IRemoteService mService;
RemoteServiceConnection mConnection = new RemoteServiceConnection();
getApplicationContext().bindService(new Intent(IRemoteService.class.getName()), mConnection, Context.BIND_AUTO_CREATE);
...where RemoteServiceConnection can be an inner class like so:
class RemoteServiceConnection implements ServiceConnection {
public void onServiceConnected(ComponentName className,
IBinder service ) {
mService = IRemoteService.Stub.asInterface(service);
isBound = true;
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
isBound = false;
}
};
And now, you're free to call..
mService.remoteDetonateBirthdayCake();
In summary: Be sure to have a service stanza in the android manifest, set "name" to the class that returns the actual implementation in its onBind() method, and you must also have an intent filter with an action definiton that points to the AIDL interface.
Hint: If you are calling remote services from an app inside a different APK, add a "category" element to the intent filter too, and set it to DEFAULT.