unbindService() does not unbind from a service? - android

I have a service that stores a Messenger as a member variable and returns messenger.getBinder() on onBind(Intent). I am connecting to this service with:
void Bind()
{
Intent intent = new Intent("com.example.RemoteBindingService");
bindService(intent, mServiceConnection,
Context.BIND_AUTO_CREATE); // Context.BIND_AUTO_CREATE
// means
// "start if not started"
mBound = true;
}
Before calling Bind(), sending a Message to the service does nothing. After calling Bind(), sending a Message to the service works correctly. However, I would expect that after calling
void Unbind()
{
if(mBound == true)
{
unbindService(mServiceConnection);
mBound = false;
}
}
that sending a Message to the service would again do nothing. However, this is not the case - the service keeps working. Can anyone explain how to properly disconnect from a service?

I assume you started your Service with startService(), because if it would be a bound Service it would get killed if the last Activity unbounds from the Service.
You can stop it
Inside the Service with stopSelf()
Outside the Service with stopService(Intent)

Related

How to start and bind a service without race conditions?

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.

Allowing service to run permanently in background

I have a messaging app based on webscokets that relies on a background service to send and receive messages. Once the service is started, I need it to run indefinitely in the background, even when the app closes as well as the phone goes into sleep mode.
In the app, the service is started just as a user logs in, and in the onCreate method as startService(new Intent(LoggingIn.this, MessagingService.class));
How can I setup my service to run permanently in the background?
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
imService = ((MessagingService.IMBinder) service).getService();
if (imService.isUserAuthenticated() == true) {
// Intent i = new Intent(LoggingIn.this, ListOfFriends.class);
Intent i = new Intent(LoggingIn.this, MainActivity.class);
startActivity(i);
LoggingIn.this.finish();
}
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
imService = null;
Toast.makeText(LoggingIn.this, R.string.local_service_stopped,
Toast.LENGTH_SHORT).show();
}
};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*
* Start and bind the imService
*/
startService(new Intent(LoggingIn.this, MessagingService.class));
...etc
Use startForeground() in your onStartCommand() method in your service.
you need to use START_STICKY in the onStartCommand:
http://developer.android.com/reference/android/app/Service.html#START_STICKY
Then you can read this thread to start your service during boot:
Android -Starting Service at Boot Time
You need to define a name for the service in the manifest to make it run in a different thread: android:process=":my_process" so it will continue to run inbackground if you close the app

Android: How to tell when a bound service has been destroyed?

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.

Hide notification of foreground service while activity is visible

Is their a way to start a service as a foreground service and hide the notification while an activity is visible?
Consider a music player, while the app is opened, you don't need the notification (with i.e. the buttons), but whenever the music player is in the background, the notification should be shown.
I know, how to do this, if I DON'T run my service in foreground... But when running in foreground, the service itself needs the notification and shows it and I can't manage the notification by myself...
How can I solve that problem?
You can do it like this. One prerequisite to this method is, that your activity must bind the service.
First you start service foreground.
private Notification mNotification;
public void onCreate() {
...
startForeground(1, mNotification);
}
Then in your activity you bind and unbind the service as shown below. BIND_ADJUST_WITH_ACTIVITY is important for keeping service alive for the time it is bound to visible activity.
public void onStart() {
...
Intent intent = new Intent(this, PlayerService.class);
bindService(intent, mConnection, BIND_ADJUST_WITH_ACTIVITY);
}
public void onStop() {
...
unbindService(mConnection);
}
Now here is the last past. You stop foreground, when at least one client is connected to the service, and you start foreground, when last client disconnects.
#Override
public void onRebind(Intent intent) {
stopForeground(true); // <- remove notification
}
#Override
public IBinder onBind(Intent intent) {
stopForeground(true); // <- remove notification
return mBinder;
}
#Override
public boolean onUnbind(Intent intent) {
startForeground(1, mNotification); // <- show notification again
return true; // <- important to trigger future onRebind()
}
When binding a service, you have to consider rules applied by Android. If you bind a not started service, the service will not start automatically unless you specify BIND_AUTO_CREATE flag in addition to BIND_ADJUST_WITH_ACTIVITY flag.
Intent intent = new Intent(this, PlayerService.class);
bindService(intent, mConnection, BIND_AUTO_CREATE
| BIND_ADJUST_WITH_ACTIVITY);
If service was started with auto creation flag on, and last client unbinds then service will stop automatically. If you want to keep service running you have to start it with startService() method. Basically, your code will look like the one below.
Intent intent = new Intent(this, PlayerService.class);
startService(intent);
bindService(intent, mConnection, BIND_ADJUST_WITH_ACTIVITY);
Calling startService() for already started service has no effect on it, as we do not override onCommand() method.
Use following steps:
1.Use ActivityManager to get current package name(i.e the Activity Running on top).
2.check if its your application then do not show the notifications
3.else If it is not your application then show the notifications.
ActivityManager manager =(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(1);
String topActivityName = tasks.get(0).topActivity.getPackageName();
if(!(topActivityName.equalsIgnoreCase("your package name"))){
//enter notification code here
}

incorrect work methods bindServer() when trying to connect to the service, after the application is restarted

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
}
}

Categories

Resources