当活动从最近的活动中被刷掉时,优雅地清理绑定的服务 [英] gracefully clean up bound service when activity is getting swiped from the recents

查看:24
本文介绍了当活动从最近的活动中被刷掉时,优雅地清理绑定的服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a bound service which goes to the foreground when needed.
Here's a simplified version of what I have:

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
    }
}

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

Then in my activity I simply do this to bind:

MyService.bind(this);

然后解除绑定:

MyService.unbind(this);

问题是,我似乎无法知道用户何时从最近的活动中刷掉了该活动.

The problem is that it seems that I have no way of knowing when the user have swiped the activity off the recent.

这是我尝试过的:

  1. 使用活动 onDestroy 方法:不,不被调用
  2. 使用活动 onPause/onStop 方法:无法真正区分滑动情况和只是转到后台,因为 isFinishing() 在所有情况下都会导致 false.
  3. 使用服务 onTaskRemovedonUnbind 方法:不被调用
  4. 在我的 AndroidManifest.xml 中,在 service 元素中添加 android:stopWithTask="true":这确实会在滑动 Activity 时终止我的服务,但是 结果是:MyActivity 已经泄露了 ServiceConnection.不确定为什么,但我的猜测是我没有机会调用 unbindService().
  1. Use the activity onDestroy method: nope, not being called
  2. Use the activity onPause/onStop methods: can't really distinguish between swipe case to just going to the background, as the isFinishing() results in false in all cases.
  3. Use the service onTaskRemoved or onUnbind methods: not being called
  4. In my AndroidManifest.xml adding android:stopWithTask="true" in the service element: that indeed kills my service when the activity is swiped, but it results in: MyActivity has leaked ServiceConnection. Not exactly sure why, but my guess is that I did not get the chance to call the unbindService().

我做错了什么或我错过了什么?
谢谢.

What am I doing wrong or what am I missing?
Thanks.

推荐答案

正如 mklimek 已经提到的,onTaskRemoved() 是解决这个问题的方法.

As already mentioned by mklimek, onTaskRemoved() is the way to go for this.

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

I use it in a project with a bound service that is started and bound by an Activity. Here are the respective parts (I'll add a bit of context to be safe):

ActivityonCreate() 调用自定义的 startService()bindService() 辅助方法:

Activity calls custom startService() and bindService() helper methods from onCreate():

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
    }

}

现在,对于 Service 类:在它的 onCreate() 中,我显示一个通知(这是运行后台服务所必需的)并设置 Binder:

Now, to the Service class: in it's onCreate(), I show a notification (which is afaik required for running background services) and set up the Binder:

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

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

    mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

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

服务接口(告诉我这是否有趣,否则我就忽略它):

Service interface (tell me if that's interesting, otherwise I just leave it out here):

private class MyServiceBinder extends MyServiceListener.Stub {

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

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

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

My onTaskRemoved() and the other lifecycle methods look as follows:

@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);
}

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

My onTaskRemoved() is called every time I swipe the Activity from the recent apps list. Are you sure your onTaskRemoved() method is not called (did you put some logging code there)? Also make sure to call the super.onTaskRemoved() method within it.

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

Our code looks quite similar, except that I put the ServiceConnection setup and Service binding code into the Activity. You moved a lot of this logic into the Service itself.

我只能猜测这可能是 Service 连接泄漏的问题,因为您的 ServiceConnection 是您的 Service 类的静态成员,并且您从 ServiceConnection 中维护对您的 Service 的引用(通过您的实例"多变的).我不确定,但是当 Activity 终止时,似乎两个链接都没有断开.请注意,在我的代码中,ServiceConnection 是 Activity 的成员,为了安全,我清除了 onServiceDisconnected() 中对 mService 的引用.也许你可以稍微重构一下,考虑到在 Activity 终止时无法 GC 的引用.

I can only guess that maybe here lies the problem with the leaking of the Service connection, as your ServiceConnection is a static member of your Service class, and you maintain a reference to your Service from within your ServiceConnection (via your "instance" variable). I'm not sure, but it seems both links are not broken, when the Activity is terminated. Mind, how in my code the ServiceConnection is a member of the Activity, and I clear the reference to mService in onServiceDisconnected() to be safe. Maybe you can refactor this a bit, taking references that cannot be GCed upon Activity termination into account.

这篇关于当活动从最近的活动中被刷掉时,优雅地清理绑定的服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆