2015-04-14 13 views
15

Nell'app Android attualmente in sviluppo, desidero connettere reti di zero-config utilizzando NsdManager.NsdManager non interrompe l'individuazione del servizio

Sono riuscito a eseguire il rilevamento dei servizi di rete e connettermi alla rete desiderata, ma dopo aver interrotto il rilevamento il thread NsdManager è ancora in esecuzione. Questo porta alla situazione quando dopo alcune rotazioni dello schermo ci sono molti thread NsdManager che stanno cercando una connessione.

enter image description here

Quando una rete è disponibile, il dispositivo tenta di sincronizzare più volte, in modo che ogni NsdManager è ancora attivo, nonostante l'arresto di rilevamento dei servizi.

Bellow è il mio codice:

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

E nel principale Activity:

private NsdTest mNsdTest; 

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

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

Come posso risolvere questo problema?

+0

Avete considerato l'utilizzo di un 'WeakReference ' per contenere il 'Contesto'? – Sebastiano

+0

@dextor Sfortunatamente non cambia nulla. Vengono creati ancora più thread. –

+0

Come si conclude che i thread 'NSDManager' stanno ancora eseguendo il rilevamento mDNS, specialmente dal momento che si dice che' onDiscoveryStopped' viene chiamato correttamente? Stai vedendo i pacchetti mDNS in uno sniffer? Potrebbe * essere il caso che la scoperta si sia arrestata ma il sistema ha mantenuto il filo, probabilmente aspettando di rilasciare alcune risorse prima di ucciderlo definitivamente. – curioustechizen

risposta

5

La mia attuale soluzione per questo è di creare una classe contenente NsdManager (NsdTest nell'esempio precedente) un singleton con contesto mutevole.

Non cambia il fatto che il thread NSdManager è in esecuzione costante anche dopo l'arresto del rilevamento dei servizi, ma almeno c'è un solo thread NsdManager attivo dopo la ripresa dell'applicazione.

Forse non è il modo più elegante, ma è abbastanza per 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; 
} 
...} 

Se qualcuno conosce la soluzione migliore, si prega di postarlo.

1

Il motivo è perché NsdManager.stopServiceDiscovery è asincrono. Da documentation, è necessario attendere onDiscoveryStopped(String) per la fine della sessione di rilevamento.

Il fatto che si interrompa immediatamente dopo l'invio della richiesta asincrona significa probabilmente che la richiamata che sarebbe stata attivata è stata "persa" mentre l'applicazione si trovava in background. I callback asincroni hanno l'abitudine di non essere gestiti in background.

+1

Non è il problema. onDiscoveryStopped (String) viene attivato correttamente ogni volta (su ogni rotazione dello schermo). –

+0

Per mia comprensione, anche se la richiamata è "persa", la scoperta dovrebbe ancora fermarsi. In altre parole, i metodi di callback dovrebbero essere richiamati dopo che la scoperta si è effettivamente interrotta. – curioustechizen

+0

@curioustechizen In un mondo perfetto che sarebbe così.Ma Android fa cose fastidiose quando fai chiamate asincrone e poi metti la tua app in background. NsdService non avrebbe potuto ricevere il post e potrebbe non aver avuto la possibilità di eseguirlo, oppure ha provato a eseguirlo ma, poiché la tua app si trovava in background, alcuni controlli non erano riusciti ed è uscito senza che tu lo sapessi. Nsd ha molte condizioni di gara, quindi sono un po 'cinico. – justhecuke

Problemi correlati