Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

bindService() returns false but unbindService() needs to be called?

My app uses a service provided by another (of my) apps. I am using a bound service wit a Messenger to access and communicate with it (and as it is a different app, it is also a remote service).

When I call bindService with a proper intent, and the call returns false (as expected when the APK providing the service is not around), I assumed from the documentation and the example code, that I do not need to unbind the ServiceConnection. However, when doing so on my Galaxy Nexus (Jelly Bean) device, I get the well-known ServiceConnectionLeaked message when finishing the Activity.

I have salvaged that this by doing

if (!ctxt.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) {
    try { 
        ctxt.unbindService(serviceConnection);
    } catch (Throwable t) {}
    // Clean up 
    return;
}
// Store serviceConnection for future use

I am curious: Did I just miss something in the documentation and is it supposed to work this way? I added the try ... catch to make sure that even if this behavior is indeed different on other devices or Android versions my app is not (negatively) affected by it.

like image 644
Stephan Avatar asked Jan 10 '13 10:01

Stephan


People also ask

What is bindService()?

It allows components (such as activities) to bind to the service, send requests, receive responses, and perform interprocess communication (IPC). A bound service typically lives only while it serves another application component and does not run in the background indefinitely.

What is the use of onBind () in android?

Binding to a Service Application components (clients) can bind to a service by calling bindService() . The Android system then calls the service's onBind() method, which returns an IBinder for interacting with the service. The binding is asynchronous.

What is bind and unbind service in Android?

Android Service: onUnbind MethodAndroid Service onUnbind method can alter the flow for Android bound service on the basis of its return value, it can either be true or false . For true it signifies that if a binding is created again for this service, instead of onBind method, onRebind method would be called.


1 Answers

General speaking, a ServiceConnection is always allocated and registered by the framework, regardless of whether bindService() call return true or false. See bindService() implementation in android.app.ContextImpl:

public boolean bindService(Intent service, ServiceConnection conn, int flags, int userHandle) {
    IServiceConnection sd;
    if (conn == null) {
        throw new IllegalArgumentException("connection is null");
    }
    if (mPackageInfo != null) {
        // A new ServiceDispatcher will be created and registered along with 
        // ServiceConnection in LoadedApk.mService for your application context.
        sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
                mMainThread.getHandler(), flags);
    } else {
        throw new RuntimeException("Not supported in system context");
    }
    try {
        ... ...
        return res != 0;
    } catch (RemoteException e) {
        return false;
    }
}

You should always unbind the service when you are done with it, as suggested by the official dev guide, as a good programming manner:

  • To disconnect from the service, call unbindService().

    When your client is destroyed, it will unbind from the service, but you should always unbind when you're done interacting with the service or when your activity pauses so that the service can shutdown while its not being used. (Appropriate times to bind and unbind is discussed more below.)

The ServiceConnectionLeaked is raised when framework start performing a final cleanup (for instance, when your app is quit) and found there are unregistered ServiceConnection, and the framework will then try to unbind it for you. See removeContextRegistrations() implementation in android.app.LoadedApk:

public void removeContextRegistrations(Context context,
        String who, String what) {
    final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled();
    ... ...
    //Slog.i(TAG, "Receiver registrations: " + mReceivers);
    HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
        mServices.remove(context);
    if (smap != null) {
        Iterator<LoadedApk.ServiceDispatcher> it = smap.values().iterator();
        while (it.hasNext()) {
            LoadedApk.ServiceDispatcher sd = it.next();
            ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
                    what + " " + who + " has leaked ServiceConnection "
                    + sd.getServiceConnection() + " that was originally bound here");
            leak.setStackTrace(sd.getLocation().getStackTrace());
            Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
            if (reportRegistrationLeaks) {
                StrictMode.onServiceConnectionLeaked(leak);
            }
            try {
                ActivityManagerNative.getDefault().unbindService(
                        sd.getIServiceConnection());
            } catch (RemoteException e) {
                // system crashed, nothing we can do
            }
            sd.doForget();
        }
    }
    mUnboundServices.remove(context);
    //Slog.i(TAG, "Service registrations: " + mServices);
}
like image 153
yorkw Avatar answered Sep 20 '22 05:09

yorkw