I want to exchange data between android devices, therefore NSD should be used to find other devices. I followed the example on Android Developers
The service is registered successfully on the network by the server-device, the client-device recognize the service and enter the discoveryListener callback method onServiceFound(). Then I initialize the resolveListener and call resolveService(), but the app crashes with
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.net.nsd.NsdManager.resolveService(android.net.nsd.NsdServiceInfo, android.net.nsd.NsdManager$ResolveListener)' on a null object reference
at de.niklasdahlheimer.oboeapitest.oboeapitest.NSDFinder$1.onServiceFound(NSDFinder.java:59)
at android.net.nsd.NsdManager$ServiceHandler.handleMessage(NsdManager.java:333)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.os.HandlerThread.run(HandlerThread.java:61)
My mNsdManager is not null in the NSDFinder class but somehow it is null in the callback-methods of the DiscoveryListener. Seems like I have a problem with different threads or a missunderstanding in working with contexts. Should my NSDFinder class extend service or something? I do not want the NSD-Code in the main-activity to keep the code well structured.
(shortened) main Activity
public class MainActivity extends AppCompatActivity {
//Variable Declarations
NSDFinder my_nsdfinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
button_discoverserver =(Button) findViewById(R.id.button_discoverServer);
//Variables Instantiations
my_nsdfinder = new NSDFinder();
button_discoverserver.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
my_nsdfinder.initializeDiscoveryListener();
my_nsdfinder.startDiscovery(getApplicationContext());
Toast.makeText(getApplicationContext(),"Suche nach Servern...",Toast.LENGTH_LONG).show();
}
});
}
}
NSDFinder class (NSDFinder.java)
public class NSDFinder{
String TAG = "NSDFINDER";
NsdManager mNsdManager;
NsdManager.DiscoveryListener mDiscoveryListener;
String mServiceName;
String SERVICE_TYPE = "_http._tcp.";
Context mContext;
int mDiscoveryActive = 0;
FragmentManager m_DialogManager;
NsdManager.ResolveListener mResolveListener;
NsdServiceInfo mService;
int mServiceport;
InetAddress mServicehostAdress;
public void initializeDiscoveryListener() {
// Instantiate a new DiscoveryListener
mDiscoveryListener = new NsdManager.DiscoveryListener() {
// Called as soon as service discovery begins.
@Override
public void onDiscoveryStarted(String regType) {
Log.d(TAG, "Service discovery started");
mDiscoveryActive = 1;
}
@Override
public void onServiceFound(NsdServiceInfo service) {
// A service was found! Do something with it.
Log.d(TAG, "Service discovery success\n" + service);
if (!service.getServiceType().equals(SERVICE_TYPE)) {
// Service type is the string containing the protocol and
// transport layer for this service.
Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
} else if (service.getServiceName().equals(mServiceName)) {
// The name of the service tells the user what they'd be
// connecting to. It could be "Bob's Chat App".
Log.d(TAG, "Same machine: " + mServiceName);
} else if (service.getServiceName().contains("LTCTool")){
Toast.makeText(mContext,"LTCTool Server gefunden! "+service,Toast.LENGTH_LONG).show();
initializeResolveListener();
mNsdManager.resolveService(service,mResolveListener);
//mNsdManager.stopServiceDiscovery(mDiscoveryListener);
}
}
@Override
public void onServiceLost(NsdServiceInfo service) {
// When the network service is no longer available.
// Internal bookkeeping code goes here.
Log.e(TAG, "service lost" + service);
}
@Override
public void onDiscoveryStopped(String serviceType) {
Log.i(TAG, "Discovery stopped: " + serviceType);
mDiscoveryActive = 0;
}
@Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
mNsdManager.stopServiceDiscovery(this);
}
@Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
mNsdManager.stopServiceDiscovery(this);
}
};
}
public void startDiscovery(Context _c){
mContext = _c;
NsdManager mNsdManager = (NsdManager) _c.getSystemService(Context.NSD_SERVICE);
mNsdManager.discoverServices(SERVICE_TYPE, mNsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
}
public void initializeResolveListener() {
Log.d(TAG, "start initalizing ResolveListener");
mResolveListener = new NsdManager.ResolveListener() {
@Override
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
// Called when the resolve fails. Use the error code to debug.
Log.e(TAG, "Resolve failed" + errorCode);
}
@Override
public void onServiceResolved(NsdServiceInfo serviceInfo) {
Log.e(TAG, "Resolve Succeeded. " + serviceInfo);
if (serviceInfo.getServiceName().equals(mServiceName)) {
Log.d(TAG, "Same IP.");
return;
}
mService = serviceInfo;
mServiceport = mService.getPort();
mServicehostAdress = mService.getHost();
Toast.makeText(mContext,"Dienstinformationen aufgelöst! Port: "+mServiceport+" Adresse: "+mServicehostAdress,Toast.LENGTH_LONG).show();
}
};
Log.d(TAG, "ResolveListener initialized");
}
}
After a few more months of coding in Java, I'm looking at this question again.
The answer is clear now. In
I called
which, instead of initialize the member-variable of the class, declared a new, local variable in the function
startDiscovery()
. SomNsdManager
was only present instartDiscovery()
. Changing the line towould probably have solved the problem.
Maybe this post will help another 'newbie' someday.
Good to see, that sometimes "mysterious problems" will be solved by time and individual progress :)