1

我有一个 LocalService,它使用一些 API 公开一个 Binder。我创建了一个服务监听器,就像这样:

if (dataServiceListener == null) {
    dataServiceListener = new DataServiceListener();
    mainActivity.getApplicationContext().bindService
        (new Intent(mainActivity, LocalService.class),
        dataServiceListener.svcConn, mainActivity.BIND_AUTO_CREATE);
}

在调用BinderindataServiceListener公开的方法后,我在方法中得到响应dataServiceListener onResult()。到目前为止,没有任何问题,一切正常。当我关闭等待服务侦听器回调的 Activity 并立即重新打开它时,会出现某种问题。即使我重新实例化dataServiceListenerin onCreate(),我得到两个回调而不是一个,旧的来自被破坏的 Activity 和后者(右)一个;这样,结果就会在 UI 上混淆。有没有办法告诉服务或服务侦听器,当活动完成时,必须避免回调。或者甚至可能销毁 ServiceListener 对象。

我认为这是 Mark L. Murphy (Commonsware) 在“The Busy Coder's Guide to Android Development”中描述的问题:

最大的收获是确保活动完成后收回侦听器。

我怎样才能做到这一点?活动结束时有没有办法摆脱无用的听众?

谢谢!

4

5 回答 5

8

我遇到过同样的问题。我在使用 AIDL 的远程服务中工作。当我尝试在 foreach 循环中使用 ArrayList 集合中的 remove 方法取消注册我的听众时遇到了这个问题,因为我没有在比较中使用 asBinder。在寻找解决方案时,我在 Android API 中找到了RemoteCallbackList类。这门课正是我需要的,我认为你应该做的,以一种简单的方式,为涉及这项任务的辛勤工作承担所有责任。

来自 Android API:

要使用此类,只需与您的服务一起创建一个实例,然后调用它的 register(E) 和 unregister(E) 方法作为客户端注册和注销您的服务。要回调已注册的客户端,请使用 beginBroadcast()、getBroadcastItem(int) 和 finishBroadcast()。

广播样本:

int i = callbacks.beginBroadcast();
while (i > 0) {
    i--;
    try {
        callbacks.getBroadcastItem(i).somethingHappened();
    } catch (RemoteException e) {
    // The RemoteCallbackList will take care of removing
    // the dead object for us.
   }
}
callbacks.finishBroadcast();
于 2012-11-05T13:29:24.063 回答
4

您显示的代码用于绑定到服务。您没有显示您在哪里注册该服务的侦听器。根据您的问题和您对onResult()方法的引用,您显然是。鉴于您的问题的性质,我猜您正在做的是:

  1. 绑定到服务onCreate()
  2. onServiceConnected()中,您正在调用某种setListener()方法Binder

在这种情况下,如果我们忽略配置更改,解决问题的正确方法是,在 中onDestroy(),调用 上的某个removeListener()方法Binder,然后调用unbindService().

配置更改,特别是在分段前的世界中,使这变得复杂。这就是为什么这个示例项目(以及书中随附的材料)如此令人讨厌的原因。绑定很棘手——如果您从旧活动中取消绑定,并且没有其他东西可以保留服务,那么服务将在新活动有机会绑定之前关闭。绑定也是状态——你不能简单地取消绑定,以免泄漏东西。

所以,配方变成:

  1. onCreate()在使用中绑定到服务Application Context
  2. onServiceConnected(),调用 sort 的setListener()方法Binder
  3. onRetainNonConfigurationInstance()中,记下您正在进行配置更改的事实,并返回一些Object具有您的Binder、您的Listener和所有其他状态的
  4. onCreate()中,使用getLastNonConfigurationInstance()-- 如果是null,则照常进行,但如果不是null,请坚持使用,Binder并且Listener不要重新绑定和重新注册侦听器
  5. onDestroy()中,如果上面第 3 步中的标志是false(即,我们没有进行配置更改),则调用removeListener()上的某个方法Binder,然后调用unbindService().

使用片段setRetainInstance(true)可能可以简化一些,尽管我还没有完成一个示例。

于 2011-08-11T12:29:55.463 回答
1

您的活动必须将自己注册/注销为侦听器。您需要使用正确的生命周期回调方法,而不是onBackPressed(). 注册onStart(),注销onStop()。一种方法是让监听器成为服务的静态成员,并提供静态注册/注销方法。然后根据需要从您的活动中调用它们。

于 2011-08-11T12:26:13.793 回答
1

我也有这个问题。完成后,您需要从服务中释放所有资源、侦听器和线程。

于 2011-08-11T12:18:29.617 回答
0

我终于解决了这个问题(不,我已经很久没有研究它了:D)。

对侦听器的回调是在调用Fragment's之前进行的onDestroy。因此布尔“dontupdate”值从未设置为 false。覆盖onBackPressed主要活动解决了这个问题,因为我为每个片段调用了一个destroy()方法,该方法负责将布尔值设置为 false。

于 2011-09-13T08:27:19.737 回答