2015-04-14 15 views
15

In Android App, die ich derzeit entwickle, möchte ich eine Verbindung Zero-Config-Netzwerke mit NsdManager.NsdManager stoppt Service-Erkennung nicht

Ich habe es geschafft, Network Service Discovery auszuführen und eine Verbindung mit dem gewünschten Netzwerk herzustellen, aber nach dem Beenden der Erkennung NsdManager Thread wird noch ausgeführt. Dies führt dazu, dass nach einigen Bildschirmdrehungen viele NsdManager Threads vorhanden sind, die nach einer Verbindung suchen.

enter image description here

Wenn ein Netzwerk verfügbar ist, versucht Gerät oft zu synchronisieren, so ist jede NsdManager noch aktiv, trotz Service Discovery zu stoppen.

Bellow ist mein 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()); 
      } 
     }; 
    } 
} 

Und in Haupt Activity:

private NsdTest mNsdTest; 

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

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

Wie kann ich dieses Problem beheben?

+0

Haben Sie darüber nachgedacht, eine 'WeakReference ' zu verwenden, um Ihren 'Context' zu halten? – Sebastiano

+0

@dextor Leider ändert sich nichts. Es werden immer noch mehrere Threads erstellt. –

+0

Wie kommen Sie zu dem Schluss, dass die 'NSDManager'-Threads tatsächlich noch die mDNS-Erkennung durchführen, besonders da Sie erwähnen, dass' onDiscoveryStopped' korrekt aufgerufen wird? Sehen Sie die mDNS-Pakete in einem Sniffer? Es könnte sein, dass die Entdeckung aufgehört hat, aber das System hat den Faden herumgehalten und wahrscheinlich darauf gewartet, einige Ressourcen freizugeben, bevor er sie schließlich tötet. – curioustechizen

Antwort

5

Meine aktuelle Lösung dafür ist, eine Klasse mit NsdManager (NsdTest im obigen Beispiel) zu einem Singleton mit wechselndem Kontext zu machen.

Es ändert nichts daran, dass der NSdManager-Thread auch nach dem Stoppen der Serviceerkennung ständig ausgeführt wird, aber nach dem Fortsetzen der Anwendung gibt es mindestens einen aktiven NsdManager-Thread.

Vielleicht ist es nicht der eleganteste Weg, aber es ist genug für mich.

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; 
} 
...} 

Wenn jemand bessere Lösung weiß, bitte posten Sie es.

1

Der Grund ist, weil NsdManager.stopServiceDiscovery asynchron ist. Von documentation müssen Sie auf onDiscoveryStopped(String) warten, damit die Ermittlungssitzung tatsächlich beendet wird.

Dass Sie unmittelbar nach dem Senden der asynchronen Anforderung pausieren, bedeutet wahrscheinlich, dass der Rückruf, der ausgelöst worden wäre, verloren gegangen ist, da sich Ihre Anwendung im Hintergrund befand. Asynchrone Rückrufe haben die Angewohnheit, nicht gut im Hintergrund zu sein.

+1

Es ist nicht das Problem. onDiscoveryStopped (String) wird jedes Mal richtig ausgelöst (bei jeder Bildschirmdrehung). –

+0

Meiner Meinung nach, selbst wenn der Rückruf "verloren" ist, sollte die Entdeckung immer noch aufhören. Mit anderen Worten, die Callback-Methoden sollten aufgerufen werden, nachdem die Erkennung tatsächlich gestoppt wurde. – curioustechizen

+0

@curioustechizen In einer perfekten Welt wäre das so.Aber Android macht wackelige Dinge, wenn Sie asynchrone Anrufe tätigen und dann Ihre App in den Hintergrund stellen. Der NsdService konnte die Post nicht erhalten haben und hatte vielleicht nicht die Möglichkeit, sie auszuführen, oder sie versuchte es auszuführen, aber da Ihre App im Hintergrund war, scheiterten einige Überprüfungen irgendwo und sie verschwanden, ohne dass Sie es wussten. Nsd hat viele Race-Bedingungen, also bin ich ein bisschen zynisch. – justhecuke