In Android
app that I'm currently developing, I want to connect do zero-config networks using NsdManager
.
I managed to run network service discovery and connect to the desired network, but after stopping discovery NsdManager
thread is still running. This leads to the situation when after a few screen rotations there are many NsdManager
threads that are browsing for a connection.
When any network is available, device tries to synchronize many times, so every NsdManager
is still active, despite stopping service discovery.
Bellow is my code:
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());
}
};
}
}
And in main Activity
:
private NsdTest mNsdTest;
@Override
protected void onResume() {
super.onResume();
mNsdTest = new NsdTest(this);
mNsdTest.startListening();
}
@Override
protected void onPause() {
mNsdTest.stopListening();
super.onPause();
}
How can I fix this problem?
My current solution for this, is to make class containing NsdManager (NsdTest in above example) a singleton with changing context.
It doesn't change the fact that NSdManager thread is constantly running even after stopping service discovery, but at least there is only one active NsdManager thread after resuming application.
Maybe it's not the most elegant way, but is enough for me.
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;
}
...}
If anybody knows better solution, please post it.
The reason is because NsdManager.stopServiceDiscovery
is asynchronous. From the documentation, you must wait for onDiscoveryStopped(String)
for the discovery session to actually end.
That you pause immediately after sending the asynchronous request probably means that the callback that would have been triggered got "lost" as your application was in the background. Asynchronous callbacks have a habit of not handling being in the background well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With