20

我在 Android 上使用网络服务发现 (NSD) 来宣传设备上的 REST 服务器。我的 REST 服务器包装在android.app.Service的一个实例中。

在中onCreate,我成功启动了 REST 服务器并注册了我的 NSD 服务。我可以在我的网络中的其他设备上看到 NSD 服务名称(例如My Service)。

在我onDestroy的 REST 服务器中,我停止了 NSD 服务的注册。

但是,似乎在开发过程中,当我推送我的应用程序的新实例(通过 Eclipse)时,并不能保证onDestroy会调用该服务。这意味着My Service仍然是广告,并且下次服务启动时,我以My Service (1)My Service (2)等结束。

我了解 NSD 将修改服务名称以创建唯一实例。我的问题是:

  1. 如果广告服务不再存在,则删除它们的预期超时是多少?
  2. 当我推送我的应用程序的新实例时,有没有办法确保我onDestroy被调用?这种情况是否类似于用户安装我的应用程序更新时可能遇到的情况?即onDestroy当正在运行的应用程序关闭时保证会被调用,以便可以安装该应用程序的新版本?
  3. 我的应用程序中是否有办法检测旧注册并将其删除?
4

2 回答 2

6

首先,在 AOSP 问题跟踪器上有一个跟踪此问题的未解决问题。提交错误的人可以在所有 Android 4.x 版本中重现,但没有使用 5.x 进行测试。

术语的简要解释:

  • NsdManager - Android 应用程序与之交互的类,用于注册其 NSD 服务并发现其他 NSD 服务。NsdManager 实际上只是一个与 NsdService 建立异步连接的 shell。
  • NsdService (com.android.server.NsdService) - 隐藏的 Android 系统服务,可以有来自多个客户端的连接。为创建和管理 NSD 服务以及将事件传回客户端应用程序进行所有繁重的工作和记账。
  • NSD 服务- NsdService 代表您创建的服务,用于通过网络宣传您的应用程序的存在。

我刚刚用 5.1 测试了这个问题,并且问题已得到修复(从源代码来看,修复似乎进入了 5.0)。我创建了一个简单的应用程序,它只注册一个 NsdService 并在发生任何值得注意的事情时记录下来,包括生命周期事件 onCreate、onResume、onPause 和 onDestroy。通过重新启动、启动我的应用程序,然后通过 Android Studio 重新安装,我得到了以下日志。

W/SimpleNsdActivity(14379): onCreate
D/NsdService( 3516): startMDnsDaemon
D/NsdService( 3516): New client listening to asynchronous messages
D/NsdService( 3516): New client, channel: com.android.internal.util.AsyncChannel
@3c114a11 messenger: android.os.Messenger@3213e776
D/NsdService( 3516): Register service
D/NsdService( 3516): registerService: 2 name: NsdTest, type: _http._tcp., host:
null, port: 1349
W/SimpleNsdActivity(14379): onResume
D/NsdService( 3516): Register 1 2
W/SimpleNsdActivity(14379): onServiceRegistered as NsdTest
D/NsdService( 3516): SERVICE_REGISTERED Raw: 606 2 "NsdTest"
D/NsdService( 3516): Client disconnected
D/NsdService( 3516): Terminating client-ID 1 global-ID 2 type 393225
D/NsdService( 3516): unregisterService: 2
D/NsdService( 3516): stopMDnsDaemon
W/SimpleNsdActivity(15729): onCreate
D/NsdService( 3516): startMDnsDaemon
D/NsdService( 3516): New client listening to asynchronous messages
D/NsdService( 3516): New client, channel: com.android.internal.util.AsyncChannel
@38bace07 messenger: android.os.Messenger@26d8cd5d
D/NsdService( 3516): Register service
D/NsdService( 3516): registerService: 3 name: NsdTest, type: _http._tcp., host:
null, port: 1349
D/NsdService( 3516): Register 1 3
W/SimpleNsdActivity(15729): onResume
D/NsdService( 3516): SERVICE_REGISTERED Raw: 606 3 "NsdTest"
W/SimpleNsdActivity(15729): onServiceRegistered as NsdTest

您可以看到既没有onPause()也没有onDestroy()被调用,因此我的应用程序没有对 NSD 服务进行显式清理。NsdService 正在检测客户端的 AsyncChannel 何时终止并正确清理它。您可以通过查看此处的源代码来更深入地了解 NsdService 日志。

总结一下,我将回答提示中的问题:

  1. 对于 4.x,没有超时,它会一直保持到重新启动。对于 5.x,NsdService 在您的应用断开连接时关闭 NSD 服务。
  2. 没有办法保证 onDestroy() 被调用,这里有更深入的解释。通过 Android Studio 安装会在不调用它的情况下杀死正在运行的应用程序。看看通过 App Store 进行的更新是否以相同的方式或更优雅地关闭正在运行的应用程序会很有趣。
  3. 在 5.x 中不是问题,因为它会立即杀死它们。在 4.x 中,无法删除旧注册,因为您会立即失去注册监听器。
于 2015-08-11T05:09:53.343 回答
0

关于第 2) 和 3) 点,您可以执行以下操作:

  • 在您的服务onCreate()方法中,生成一个随机数或时间戳并将其保存到本地字段;这将是服务ID
  • BroadcastReceiver在您的 中注册一个自定义Service,它将对您的自定义ACTION做出反应
  • 在每次创建时,使用您的自定义ACTION和您当前的ID作为额外参数Service发送一个广播Service
  • 对于每一个 active ServiceBroadcastReceiver都会触发注册的;现在将接收到的ID与当前服务的ID进行比较
    • 如果它们不同,调用stopSelf()

这样,只有最后一个Service会活着。

于 2015-08-07T10:06:42.087 回答