Bind/Unbind Service Example (Android)

bind/unbind service example (android)

You can try using this code:

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(intent, mServerConn, Context.BIND_AUTO_CREATE);
mContext.startService(intent);
}

public void stop() {
mContext.stopService(new Intent(mContext, ServiceRemote.class));
mContext.unbindService(mServerConn);
}

Android: How to safely unbind a service

Try this:

boolean isBound = false;
...
isBound = getApplicationContext().bindService( new Intent(getApplicationContext(), ServiceUI.class), serviceConnection, Context.BIND_AUTO_CREATE );
...
if (isBound)
getApplicationContext().unbindService(serviceConnection);

Note:

You should use same context for binding a service and unbinding a service. If you are binding Service with getApplicationContext() so you should also use getApplicationContext.unbindService(..)

Why unbind Service onDestroy?

Activities need to handle configuration changes, such as when the screen is rotated, or the user changes locales, or the device enters night mode.

The default behavior of the foreground activity, when a configuration change occurs, is for it to be destroyed and recreated.

As a result, calling bindService() on an Activity is not a good idea. We want the binding to remain intact across the configuration change. Otherwise, our service will get destroyed and recreated, along with the activity (assuming that the activity has the one-and-only binding and nothing else started the service).

So, the recommended pattern is to call bindService() on the Application singleton. Then, you can pass your ServiceConnection from the old activity instance to the new activity instance. Retained fragments work great for this, as you can then call unbindService() in onDestroy() of the fragment, so that when the activity is "permanently" destroyed (e.g., user presses BACK, you call finish()), your binding can be released.


With all that as background, on to your specific concern.

First, you assume that a destroyed activity automatically unbinds from any services that it bound to via bindService() called on that Activity. It's possible that this happens, though I do not recall that being documented behavior, and it's the sort of thing that developers should not rely upon.

More importantly, in most cases, calling bindService() on the Activity is not the right approach. Otherwise, you get into the problems that I outlined above.

But, following the call-bindService()-on-the-Application pattern, I would not expect there ever to be some sort of automatic unbinding, because the Application singleton is never destroyed. So, if you fail to call unbindService() somewhere (e.g., in onDestroy() of the retained fragment), you will leak your service.

Cleanly binding/unbinding to a Service in an Application

I solved this problem by counting the references to the service binding in the Application. Every Activity has to call acquireBinding() in their onCreate() methods and call releaseBinding() in onDestroy(). If the reference counter reaches zero the binding is released.

Here's an example:

class MyApp extends Application {
private final AtomicInteger refCount = new AtomicInteger();
private Binding binding;

@Override
public void onCreate() {
// create service binding here
}

public Binding acquireBinding() {
refCount.incrementAndGet();
return binding;
}

public void releaseBinding() {
if (refCount.get() == 0 || refCount.decrementAndGet() == 0) {
// release binding
}
}
}

// Base Activity for all other Activities
abstract class MyBaseActivity extend Activity {
protected MyApp app;
protected Binding binding;

@Override
public void onCreate(Bundle savedBundleState) {
super.onCreate(savedBundleState);
this.app = (MyApp) getApplication();
this.binding = this.app.acquireBinding();
}

@Override
public void onDestroy() {
super.onDestroy();
this.app.releaseBinding();
}
}

Should an Activity unbind from a started Foreground Service in onPause()?

Since setting up the ServiceConnection takes some time, binding to the Service in onResume() may be way too late for your app to work smoothly. In addition to that, onPause() will also be called e.g. when you show an AlertDialog, so unbinding there means you'd have to "re-bind" as soon as the dialog is dismissed and - again - wait for the ServiceConnection to be up and running.

The documentation on Bound Services states

You usually pair the binding and unbinding during matching bring-up
and tear-down moments of the client's lifecycle, as described in the
following examples:

  • If you need to interact with the service only while your activity is visible, you should bind during onStart() and unbind during
    onStop().
  • If you want your activity to receive responses even while it is stopped in the background, then you can bind during onCreate() and
    unbind during onDestroy(). Beware that this implies that your activity
    needs to use the service the entire time it's running (even in the
    background), so if the service is in another process, then you
    increase the weight of the process and it becomes more likely that the
    system will kill it.

So in your case you can use onStart()/ onStop()

When should I use unbindService(), and how should I use it properly to unbind from a remote service that is using an AIDL interface?

First, unless you actually need to make calls to this service across processes (that is, from other .apks, or you are using android:process to split up your own .apk into multiple processes for some reason), then I really recommend just dropping the use of aidl. It is more complexity for no gain. The "Local Service Sample" in the Service documentation shows how to do this: http://developer.android.com/reference/android/app/Service.html

Second, doing a bind at the same time as a start is a strong indication of some basic flaw in the design. Starting a service and binding to a service are semantically very different, so will be done at different places based on those different semantics. That is, if both are even done at all... in fact it is an unusual situation where you are using both start and bind with the same service.

In the class implementation of a service for doing music playback, it would use start when it is actively performing playback (so its process doesn't get killed by the system when the user is no longer actively interacting with the application's UI). Starting the service when the user has enters the UI is likely to cause pain because now the start/stopped state of the service is not clearly defined -- it could be started either because it is doing playback or because the user happens to have gone into the app's UI, and now when is the right time to stop it? This is going to be troublesome.

Now as far as when to unbind -- you just need to make sure you always match an unbindService() with a previous bindService(). From your snippets of code it looks like you are doing this, but there are strange things in it like mBound never being set. In fact if you are consistently binding in onStart() and unbinding in onStop(), you should never need to have an mBound to decide whether to unbind, because onStop() is always called after onStart().

So with the code you give here, it doesn't look like there is a problem. If you are getting exceptions, though, there clearly is so it may be elsewhere in your app. To help narrow the problem down, you can use this flag when you call bindService() to get additional information in the log when the failure happens: http://developer.android.com/reference/android/content/Context.html#BIND_DEBUG_UNBIND



Related Topics



Leave a reply



Submit