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.
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.
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.
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.
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With