当 activity 从最近更新中被刷掉时,优雅地清理绑定服务

gracefully clean up bound service when activity is getting swiped from the recents

我有一个绑定服务,它在需要时转到前台。
这是我所拥有的简化版本:

class MyService extends Service {
    private static final ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder serviceBinder) {
            instance.bound();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            instance.unbound();
        }
    };
    private static MyService instance;

    public MyService() {
        instance = this;
    }

    public static boolean bind(final Context context) {
        final Intent intent = new Intent(context, MyService.class);
        return context.bindService(intent, MyService.serviceConnection, Context.BIND_AUTO_CREATE);
    }

    public static void unbind(final Context context) {
        context.unbindService(MyService.serviceConnection);
    }

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        // not being called
    }

    @Override
    public boolean onUnbind(Intent intent) {
        // not being called
    }

    private void bound() {}

    private void unbound() {
        // not being called
    }
}

然后在我的 activity 中,我只是这样做来绑定:

MyService.bind(this);

解绑:

MyService.unbind(this);

问题是我似乎无法知道用户什么时候刷掉了最近的 activity。

这是我尝试过的:

  1. 使用activity onDestroy方法:不,没有被调用
  2. 使用 activity onPause/onStop 方法:无法真正区分滑动大小写和直接进入后台,因为 isFinishing() 导致 false 在所有情况下。
  3. 使用服务onTaskRemovedonUnbind方法:未被调用
  4. 在我的 AndroidManifest.xml 中,在服务元素中添加 android:stopWithTask="true":当 activity 被滑动时,确实会终止我的服务, 但是 它结果:MyActivity has leaked ServiceConnection。不确定为什么,但我猜我没有机会调用 unbindService().

我做错了什么或者我错过了什么?
谢谢。

正如 mklimek 已经提到的,onTaskRemoved() 是解决此问题的方法。

我在一个项目中使用它,该项目具有由 Activity 启动和绑定的绑定服务。以下是各个部分(为了安全起见,我将添加一些上下文):

ActivityonCreate():

调用自定义 startService()bindService() 辅助方法
private void startService() {
    Intent myServiceIntent = new Intent(this, packageName.MyService.class);
    startService(myServiceIntent);
}

private void bindService() {
    mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mService = MyServiceListener.Stub.asInterface(iBinder);

            try {
                mService.registerCallback(myServiceCallback);
                mService.doSomething();
            } catch (RemoteException e) {
                MLog.w(TAG, "Exception on callback registration; Service has probably crashed.");
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mService = null;
        }
   };

    if(!myServiceIsBound) {
        Intent myServiceIntent = new Intent(this, packageName.MyService.class);
        bindService(myServiceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
        myServiceIsBound = true;
        // service is now bound
    } else {
        // service has already been bound
    }

}

现在,服务 class:在它的 onCreate() 中,我显示了一个通知(据我所知,运行 背景服务)并设置活页夹:

@Override
public void onCreate() {
    super.onCreate();

    // Setup Binder for IPC
    mBinder = new MyServiceBinder();

    mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

    [...display notification code...] 
}

服务接口(告诉我这是否有趣,否则我就把它留在这里):

private class MyServiceBinder extends MyServiceListener.Stub {

    @Override
    public void registerCallback(MyServiceCallback callback) throws RemoteException {
         [...]
    }

    // further methods for the service interface...
}

我的 onTaskRemoved() 和其他生命周期方法如下所示:

@Override
public void onTaskRemoved(Intent rootIntent) {
    super.onTaskRemoved(rootIntent);

    // do something adequate here
}


// Lifecycle management
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    return START_REDELIVER_INTENT;
}


// Binding
@Override
public IBinder onBind(Intent intent) {
    return mBinder;
}

@Override
public boolean onUnbind(Intent intent) {
    return super.onUnbind(intent);
}

每次我从最近的应用程序列表中滑动 Activity 时都会调用我的 onTaskRemoved()。您确定没有调用您的 onTaskRemoved() 方法(您是否在其中放置了一些日志记录代码)?还要确保在其中调用 super.onTaskRemoved() 方法。

我们的代码看起来很相似,只是我将 ServiceConnection 设置和服务绑定代码放入了 Activity。您将很多这种逻辑移到了服务本身。

我只能猜测这可能是服务连接泄漏的问题,因为您的 ServiceConnection 是服务的静态成员 class,并且您在自己的服务中维护对服务的引用ServiceConnection(通过您的 "instance" 变量)。我不确定,但是当 Activity 终止时,两个链接似乎都没有断开。请注意,在我的代码中,ServiceConnection 如何成为 Activity 的成员,为了安全起见,我清除了 onServiceDisconnected() 中对 mService 的引用。也许您可以稍微重构一下,考虑在 Activity 终止时无法进行 GC 的引用。