21

I'm attempting to use a Service in Android for some basic database operations, but for some reason I'm getting an Activity has leaked ServiceConnection error. I'll post the full Logcat readout at the bottom.

I have to use the same service in multiple activities, so I've created a Superclass to handle all of the service tasks. It looks like this:

private MyInterface child;

public void onCreate(Bundle savedInstanceState, MyInterface child){
    super.onCreate(savedInstanceState);

    doBindService();
}

public void onResume(){
    super.onResume();

    doBindService();
}

protected void onPause(){
    super.onPause();

    doUnbindService();
}

private boolean bound;
private boolean binding;

ServiceConnection Connection = new ServiceConnection(){

    //called when the service is connected
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.d(LOGTAG, "Bound to Service");
        bound = true;
        binding = false;
        toServiceMessenger = new Messenger(service);
        while(!commandsForService.isEmpty()){
            sendToService(commandsForService.poll());
        }
    }

    //called when the service is disconnected
    @Override
    public void onServiceDisconnected(ComponentName name) {
        Log.d(LOGTAG, "Unboud from Service");
        bound = false;
        binding = false;
        toServiceMessenger = null;
    }
};

private boolean doBindService(){
    Log.d(LOGTAG, "Attempting to Bind to Service");
    if(!bound && !binding){
        binding = true;
        bindService(new Intent(this, global.FetchService.class), Connection, Context.BIND_AUTO_CREATE);
    }
    return bound;
}

private void doUnbindService(){
    Log.d(LOGTAG, "Attempting to Unbind from Service");
    if(bound && !binding){
        binding = true;
        unbindService(Connection);
    }
}

public void sendToService(Message msg){
    if(bound){
        sendMessageToService(msg);
    }
    else{
        commandsForService.add(msg);
    }
}

private void sendMessageToService(Message msg){
    if(bound){
        msg.replyTo = fromServiceMessenger;
        try {
            toServiceMessenger.send(msg);
        } catch (RemoteException e) {
            Log.d(LOGTAG, "RemoteException communicating with service");
        }
    }
    else{
        Log.d(LOGTAG, "Error: toServiceMessenger null while bound");
    }
}

The idea is that the child activity will never need to worry about being connected to the service or not, the Superclass should take care of getting data the service and back to the child.

The Logcat points doBindService() in onCreate() --> bindService(new Intent(this, global.FetchService.class), Connection, Context.BIND_AUTO_CREATE); as the line causing the error. However, the service only leaks after the activity has been running and visible for longer than 15 seconds, so I don't think that onCreate() should have been called.

Here's the Logcat:

09-02 09:25:40.635: E/ActivityThread(5963): Activity childActivity has leaked ServiceConnection superClass$1@42cb1b70 that was originally bound here
09-02 09:25:40.635: E/ActivityThread(5963): android.app.ServiceConnectionLeaked: Activity childActivity has leaked ServiceConnection superClass$1@42cb1b70 that was originally bound here
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.LoadedApk$ServiceDispatcher.<init>(LoadedApk.java:1055)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:949)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.ContextImpl.bindService(ContextImpl.java:1472)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.ContextImpl.bindService(ContextImpl.java:1464)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.content.ContextWrapper.bindService(ContextWrapper.java:394)
09-02 09:25:40.635: E/ActivityThread(5963):     at superClass.doBindService(FetchActivity.java:253)
09-02 09:25:40.635: E/ActivityThread(5963):     at superClass.onCreate(FetchActivity.java:61)
09-02 09:25:40.635: E/ActivityThread(5963):     at childActivity.onCreate(Showcase_Activity.java:37)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.Activity.performCreate(Activity.java:5066)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1101)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2311)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2391)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.ActivityThread.access$600(ActivityThread.java:151)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1335)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.os.Handler.dispatchMessage(Handler.java:99)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.os.Looper.loop(Looper.java:155)
09-02 09:25:40.635: E/ActivityThread(5963):     at android.app.ActivityThread.main(ActivityThread.java:5493)
09-02 09:25:40.635: E/ActivityThread(5963):     at java.lang.reflect.Method.invokeNative(Native Method)
09-02 09:25:40.635: E/ActivityThread(5963):     at java.lang.reflect.Method.invoke(Method.java:511)
09-02 09:25:40.635: E/ActivityThread(5963):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
09-02 09:25:40.635: E/ActivityThread(5963):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:795)
09-02 09:25:40.635: E/ActivityThread(5963):     at dalvik.system.NativeStart.main(Native Method)

Any help will be greatly appreciated.

MrMannWood
  • 566
  • 1
  • 4
  • 15
  • Does ServiceConnectionLeaked occur after you leave the childActivity? Could you put Log statements immediately after bindService and unbindService to make sure they are being called as matched pairs? – Peter Tran Nov 20 '13 at 19:31
  • Maybe try calling doUnbindService() *before* calling super.onPause() (in your onPause() method)? – Ponkadoodle May 22 '14 at 01:49

5 Answers5

18

As you call the doBindService() in the onCreate(), you should add the do UnbindService() in onDestroy().

It works for me.

try-catch-finally
  • 7,436
  • 6
  • 46
  • 67
BinqiangSun
  • 680
  • 6
  • 15
  • Sorry it took me so long to respond. For people coming in the future: the solution is that the calls to doBindService() and doUnbindService() should be mirrored in the Android Activity lifecycle. Because binding to a service can take a while, it should be done in onCreate() and onDestroy(). I was doing it in onResume() and onPause() because I was paranoid about the Android lifecycle. – MrMannWood Sep 17 '15 at 12:20
  • @MrMannWood Please take a look at my updated [solution](https://stackoverflow.com/a/56607547/2253682) below in order to see how to handle this for newer API levels. – AdamHurwitz Jun 15 '19 at 05:16
4

You're calling doBindService() in both onCreate() and onResume(), trying calling it just in onResume() to match your call to doUnbindService() in onPause()

bclymer
  • 6,679
  • 2
  • 27
  • 36
  • I just tried this and it didn't work, the Logcat now points to the doBindService in onResume(). I wouldn't expect this to be a problem anyway, as my booleans should make sure that the call doesn't happen both times. doBindService is in both to make sure that a) the service is always connected when the activity is visible and b) the service connects quickly when the activity is being created – MrMannWood Sep 02 '13 at 15:12
2

You need to use the good reference for the Activity getActivity() return the current one, and you need to unbind on the one which did the bind

here's how I fixed the problem :

private Activity mActivity;
    @Override
    public void onStart() {
        super.onStart();
        if (!mBound) {
            startService();
            mActivity = getActivity();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mBound && mActivity != null) {
            mActivity.unbindService(mServiceConnection);
            mActivity = null;
        }
    }
Dounaka
  • 21
  • 1
1

You are getting this error because service which you bind with activity is not stopped when that activity destroyed. So due this your service is still exist in memory and consuming resources, which is doing nothing.

To solve this problem call doBindService() in onCreate() and doUnbindService() in onDestroy()

Vijay Vankhede
  • 3,018
  • 1
  • 26
  • 46
0

This issue is limited to Android SDKs prior to Android O, API level 26. In Android O the following function to launch foreground services was introduced ContextCompat.startForegroundService(...) as opposed to context.startService(...). The older APIs do not handle the foreground lifecycle, thus the need to call context?.unbindService(serviceConnection) in onDestroy(...).

The new method ensures developers must create a notification with startForeground(notificationId, notification) within five seconds of calling ContextCompat.startForegroundService(...) in order to ensure the user is aware of the foreground service running even when the app is not in view.

For any devices running Nougat API level 25 or lower make sure to unbind the service manually.

ie

override fun onDestroy() {
    if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.N_MR1) 
        context?.unbindService(serviceConnection)
}
AdamHurwitz
  • 9,758
  • 10
  • 72
  • 134