【问题标题】:NsdManager doesn't stop service discoveryNsdManager 不会停止服务发现
【发布时间】:2015-04-14 13:08:10
【问题描述】:

在我目前正在开发的Android 应用程序中,我想使用NsdManager 连接做零配置网络。

我设法运行网络服务发现并连接到所需的网络,但停止发现后NsdManager 线程仍在运行。这会导致在几次屏幕旋转后有许多NsdManager 线程正在浏览连接。

当任何网络可用时,设备会尝试多次同步,因此每个NsdManager 仍然处于活动状态,尽管服务发现已停止。

下面是我的代码:

package dtokarzewsk.nsdservicetest;

import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.util.Log;
import java.net.InetAddress;

public class NsdTest {
    private static final String NSD_SERVICE_NAME = "TestService";
    private static final String NSD_SERVICE_TYPE = "_http._tcp.";
    private int mPort;
    private InetAddress mHost;
    private Context mContext;
    private NsdManager mNsdManager;
    private android.net.nsd.NsdManager.DiscoveryListener mDiscoveryListener;
    private android.net.nsd.NsdManager.ResolveListener mResolveListener;

    public NsdTest(Context context) {
        mContext = context;
    }

    public void startListening() {
        initializeResolveListener();
        initializeDiscoveryListener();
        mNsdManager = (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
        mNsdManager.discoverServices(NSD_SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
    }

    public void stopListening() {
        mNsdManager.stopServiceDiscovery(mDiscoveryListener);
    }

    private void initializeResolveListener() {
        mResolveListener = new NsdManager.ResolveListener() {
            @Override
            public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
                Log.d("NSDService test","Resolve failed");
            }

            @Override
            public void onServiceResolved(NsdServiceInfo serviceInfo) {
                NsdServiceInfo info = serviceInfo;
                Log.d("NSDService test","Resolve failed");
                mHost = info.getHost();
                mPort = info.getPort();
                Log.d("NSDService test","Service resolved :" + mHost + ":" + mPort);
            }
        };
    }

    private void initializeDiscoveryListener() {
        mDiscoveryListener = new NsdManager.DiscoveryListener() {
            @Override
            public void onStartDiscoveryFailed(String serviceType, int errorCode) {
                Log.d("NSDService test","Discovery failed");
            }

            @Override
            public void onStopDiscoveryFailed(String serviceType, int errorCode) {
                Log.d("NSDService test","Stopping discovery failed");
            }

            @Override
            public void onDiscoveryStarted(String serviceType) {
                Log.d("NSDService test","Discovery started");
            }

            @Override
            public void onDiscoveryStopped(String serviceType) {
                Log.d("NSDService test","Discovery stopped");
            }

            @Override
            public void onServiceFound(NsdServiceInfo serviceInfo) {
                NsdServiceInfo info = serviceInfo;
                Log.d("NSDService test","Service found: " + info.getServiceName());
                if (info.getServiceName().equals(NSD_SERVICE_NAME))
                    mNsdManager.resolveService(info, mResolveListener);
            }

            @Override
            public void onServiceLost(NsdServiceInfo serviceInfo) {
                NsdServiceInfo info = serviceInfo;
                Log.d("NSDService test","Service lost: " + info.getServiceName());
            }
        };
    }
}

主要是Activity:

private NsdTest mNsdTest;

@Override
protected void onResume() {
    super.onResume();
    mNsdTest = new NsdTest(this);
    mNsdTest.startListening();
}

@Override
protected void onPause() {
    mNsdTest.stopListening();
    super.onPause();
}

我该如何解决这个问题?

【问题讨论】:

  • 您是否考虑过使用WeakReference<Context> 来保存您的Context
  • @dextor 不幸的是它并没有改变任何东西。仍在创建多个线程。
  • 您如何断定NSDManager 线程实际上仍在执行mDNS 发现,特别是因为您提到onDiscoveryStopped 被正确调用?您是否在嗅探器中看到 mDNS 数据包? 可能发现已经停止但系统保留了线程,可能在最终杀死它之前等待释放一些资源。
  • 我没有说线程仍在执行发现。实际上他们没有。问题是在停止发现后,即使经过很长时间,它们也不会被杀死。在极端情况下,当用户执行 20-30 次屏幕旋转时,会打开 20-30 个线程并杀死它们应用程序崩溃。

标签: android android-lifecycle android-nsd


【解决方案1】:

我目前的解决方案是使包含 NsdManager 的类(上例中的 NsdTest)成为一个具有变化上下文的单例。

这并没有改变 NSdManager 线程即使在停止服务发现后也一直在运行的事实,但至少在恢复应用程序后只有一个活动的 NsdManager 线程。

也许这不是最优雅的方式,但对我来说已经足够了。

public class NsdTest {

private static NsdTest mInstance;

private static final String NSD_SERVICE_NAME = "TestService";
private static final String NSD_SERVICE_TYPE = "_http._tcp.";
private int mPort;
private InetAddress mHost;
private Context mContext;
private NsdManager mNsdManager;
private android.net.nsd.NsdManager.DiscoveryListener mDiscoveryListener;
private android.net.nsd.NsdManager.ResolveListener mResolveListener;
private static NsdTest mInstance;

private NsdTest(Context context) {
    mContext = context;
}

public static NsdTest getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new NsdTest(context);
    } else {
       mContext = context;
    }
    return mInstance;
}
...}

如果有人知道更好的解决方案,请发布。

【讨论】:

  • 我不知道更好的解决方案,但我确实注意到 NSD_SERVICE_TYPE = "_http._tcp." 在值的末尾似乎有一个额外的点 (.)。我不知道这是否有任何区别,但文档仅在中间显示一个点,而不是在末尾。
  • 我尝试了带点和不带点的两个版本,它的作用相同。
  • 我想我正在与同样的事情作斗争。我注意到我的 android 客户端需要 forever 才能找到我的服务。重新启动它们似乎是唯一的治疗方法。我正要借你的修理,看看有没有用。额外的点也是每个规格。
  • 这里真的很愚蠢的问题,但聪明的人还没有解决它。获取 NSDManager 的调用需要一个 Context 对象。你传入的是 ApplicationContext 而不是 Activity 的,对吧?
【解决方案2】:

原因是因为NsdManager.stopServiceDiscovery 是异步的。从documentation 开始,您必须等待onDiscoveryStopped(String) 才能真正结束发现会话。

您在发送异步请求后立即暂停可能意味着本应触发的回调“丢失”了,因为您的应用程序在后台。异步回调有一个习惯,就是不能很好地处理在后台。

【讨论】:

  • 这不是问题。 onDiscoveryStopped(String) 每次都会正确触发(在每次屏幕旋转时)。
  • 根据我的理解,即使回调“丢失”,发现仍然应该停止。换句话说,回调方法应该在发现实际停止后调用。
  • @curioustechizen 在一个完美的世界里会是这样。但是当您进行异步调用然后将您的应用程序置于后台时,Android 会做一些不稳定的事情。 NsdService 无法收到 Post 并且可能没有机会运行它,或者它尝试运行它,但是由于您的应用程序在后台,某些地方的检查失败并且它在您不知情的情况下退出。 Nsd 有很多竞争条件,所以我有点愤世嫉俗。
猜你喜欢
  • 2018-07-06
  • 2014-08-31
  • 1970-01-01
  • 1970-01-01
  • 2014-01-13
  • 2013-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多