Utilizza il rilevamento dei servizi di rete

Network Service Discovery (NSD) consente alla tua app di accedere a servizi dai dispositivi connessi a una rete locale. I dispositivi che supportano NSD includono stampanti, webcam, server HTTPS e altri dispositivi mobili.

NSD implementa il meccanismo di Service Discovery (DNS-SD) basato su DNS, che consente alla tua app di richiedere servizi specificando un tipo di servizio e il nome di un'istanza di dispositivo che fornisce il tipo di servizio desiderato. DNS-SD corrente supportata sia su Android che su altre piattaforme mobile.

L'aggiunta di NSD alla tua app consente agli utenti di identificare altri dispositivi sulla che supporti i servizi richiesti dalla tua app. Questo è utile per una varietà di applicazioni peer-to-peer, come la condivisione di file o la modalità multiplayer gaming. Le API NSD di Android semplificano l'impegno richiesto per l'implementazione tali funzionalità.

Questa lezione mostra come creare un'applicazione in grado di trasmettere nome e informazioni di connessione alla rete locale e cercare informazioni da altre applicazioni che fanno lo stesso. Infine, questa lezione mostra di connettersi alla stessa applicazione in esecuzione su un altro dispositivo.

Registrazione del servizio sulla rete

Nota: questo passaggio è facoltativo. Se non ti interessa trasmettere i servizi della tua app sulla rete locale, puoi passare direttamente prossima sezione, Scopri i servizi sulla rete.

Per registrare il tuo servizio sulla rete locale, devi prima creare un oggetto NsdServiceInfo. Questo oggetto fornisce le informazioni usati dagli altri dispositivi sulla rete per decidere se connettersi o meno al tuo completamente gestito di Google Cloud.

Kotlin

fun registerService(port: Int) {
    // Create the NsdServiceInfo object, and populate it.
    val serviceInfo = NsdServiceInfo().apply {
        // The name is subject to change based on conflicts
        // with other services advertised on the same network.
        serviceName = "NsdChat"
        serviceType = "_nsdchat._tcp"
        setPort(port)
        ...
    }
}

Java

public void registerService(int port) {
    // Create the NsdServiceInfo object, and populate it.
    NsdServiceInfo serviceInfo = new NsdServiceInfo();

    // The name is subject to change based on conflicts
    // with other services advertised on the same network.
    serviceInfo.setServiceName("NsdChat");
    serviceInfo.setServiceType("_nsdchat._tcp");
    serviceInfo.setPort(port);
    ...
}

Questo snippet di codice imposta il nome del servizio su "NsdChat". Il nome del servizio è il nome dell'istanza, ovvero il nome visibile agli altri dispositivi nella rete. Il nome è visibile a qualsiasi dispositivo nella rete che utilizza NSD per cercare servizi locali. Tieni presente che il nome deve essere univoco per qualsiasi servizio della e Android gestisce automaticamente la risoluzione dei conflitti. Se su due dispositivi sulla rete sia installata l'applicazione NsdChat, uno dei cambia automaticamente il nome del servizio in qualcosa come "NsdChat (1)".

Il secondo parametro imposta il tipo di servizio, specifica il protocollo e il trasporto il livello utilizzato dall'applicazione. La sintassi è "_<protocollo>._<transportlayer>". Nella snippet di codice, il servizio utilizza il protocollo HTTP in esecuzione su TCP. Un'applicazione che offre un servizio di stampa (ad esempio, una stampante di rete) imposta in "_ipp._tcp".

Nota: i numeri assegnati internazionali Authority (IANA) gestisce un sistema di gestione elenco autorevoli dei tipi di servizi utilizzati dai protocolli di Service Discovery come NSD e Bonjour. Puoi scaricare l'elenco dal Elenco IANA dei nomi dei servizi e dei numeri di porta Se intendi utilizzare un nuovo tipo di servizio, devi prenotarlo compilando IANA Ports and Service modulo di registrazione.

Quando imposti la porta per il tuo servizio, evita l'hardcoded della porta perché questa sono in conflitto con altre applicazioni. Ad esempio, supponendo l'applicazione usa sempre la porta 1337 e la mette in potenziale conflitto con e le altre applicazioni installate che usano la stessa porta. Utilizza invece il metodo la prossima porta disponibile. Poiché queste informazioni vengono fornite ad altre app da un di servizio, non c'è bisogno della porta utilizzata dall'applicazione noti da altre applicazioni al momento della compilazione. Le applicazioni possono invece queste informazioni provenienti dalla trasmissione del tuo servizio, subito prima di connetterti al tuo completamente gestito di Google Cloud.

Se utilizzi i socket, di seguito puoi scoprire come inizializzare un socket a qualsiasi disponibile solo su 0.

Kotlin

fun initializeServerSocket() {
    // Initialize a server socket on the next available port.
    serverSocket = ServerSocket(0).also { socket ->
        // Store the chosen port.
        mLocalPort = socket.localPort
        ...
    }
}

Java

public void initializeServerSocket() {
    // Initialize a server socket on the next available port.
    serverSocket = new ServerSocket(0);

    // Store the chosen port.
    localPort = serverSocket.getLocalPort();
    ...
}

Ora che hai definito l'oggetto NsdServiceInfo, devi implementare l'interfaccia RegistrationListener. Questo contiene i callback utilizzati da Android per avvisare l'applicazione del esito positivo o negativo della registrazione del servizio e dell'annullamento della registrazione.

Kotlin

private val registrationListener = object : NsdManager.RegistrationListener {

    override fun onServiceRegistered(NsdServiceInfo: NsdServiceInfo) {
        // Save the service name. Android may have changed it in order to
        // resolve a conflict, so update the name you initially requested
        // with the name Android actually used.
        mServiceName = NsdServiceInfo.serviceName
    }

    override fun onRegistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
        // Registration failed! Put debugging code here to determine why.
    }

    override fun onServiceUnregistered(arg0: NsdServiceInfo) {
        // Service has been unregistered. This only happens when you call
        // NsdManager.unregisterService() and pass in this listener.
    }

    override fun onUnregistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
        // Unregistration failed. Put debugging code here to determine why.
    }
}

Java

public void initializeRegistrationListener() {
    registrationListener = new NsdManager.RegistrationListener() {

        @Override
        public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {
            // Save the service name. Android may have changed it in order to
            // resolve a conflict, so update the name you initially requested
            // with the name Android actually used.
            serviceName = NsdServiceInfo.getServiceName();
        }

        @Override
        public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
            // Registration failed! Put debugging code here to determine why.
        }

        @Override
        public void onServiceUnregistered(NsdServiceInfo arg0) {
            // Service has been unregistered. This only happens when you call
            // NsdManager.unregisterService() and pass in this listener.
        }

        @Override
        public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
            // Unregistration failed. Put debugging code here to determine why.
        }
    };
}

Ora hai tutti i componenti per registrare il tuo servizio. Chiama il metodo registerService().

Tieni presente che questo metodo è asincrono, quindi qualsiasi codice da eseguire Dopo la registrazione del servizio, devi usare il metodo onServiceRegistered().

Kotlin

fun registerService(port: Int) {
    // Create the NsdServiceInfo object, and populate it.
    val serviceInfo = NsdServiceInfo().apply {
        // The name is subject to change based on conflicts
        // with other services advertised on the same network.
        serviceName = "NsdChat"
        serviceType = "_nsdchat._tcp"
        setPort(port)
    }

    nsdManager = (getSystemService(Context.NSD_SERVICE) as NsdManager).apply {
        registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener)
    }
}

Java

public void registerService(int port) {
    NsdServiceInfo serviceInfo = new NsdServiceInfo();
    serviceInfo.setServiceName("NsdChat");
    serviceInfo.setServiceType("_http._tcp.");
    serviceInfo.setPort(port);

    nsdManager = Context.getSystemService(Context.NSD_SERVICE);

    nsdManager.registerService(
            serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener);
}

Scopri i servizi sulla rete

La rete brulica di vita, dalle bestionali stampanti di rete ai di webcam, per le brutali e infuocate battaglie dei tris giocatori. La chiave per far vedere alla tua applicazione questo vivace ecosistema di è Service Discovery. La tua applicazione deve ascoltare il servizio gli annunci in rete per vedere quali servizi sono disponibili e filtrare tutto ciò con cui l'applicazione non può funzionare.

Il rilevamento dei servizi, ad esempio la registrazione, prevede due passaggi: impostando un listener di rilevamento con i callback pertinenti e creando un singolo Chiamata API a discoverServices().

Innanzitutto, crea un'istanza per un corso anonimo che implementa NsdManager.DiscoveryListener. Il seguente snippet mostra un esempio semplice:

Kotlin

// Instantiate a new DiscoveryListener
private val discoveryListener = object : NsdManager.DiscoveryListener {

    // Called as soon as service discovery begins.
    override fun onDiscoveryStarted(regType: String) {
        Log.d(TAG, "Service discovery started")
    }

    override fun onServiceFound(service: NsdServiceInfo) {
        // A service was found! Do something with it.
        Log.d(TAG, "Service discovery success$service")
        when {
            service.serviceType != SERVICE_TYPE -> // Service type is the string containing the protocol and
                // transport layer for this service.
                Log.d(TAG, "Unknown Service Type: ${service.serviceType}")
            service.serviceName == 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")
            service.serviceName.contains("NsdChat") -> nsdManager.resolveService(service, resolveListener)
        }
    }

    override fun onServiceLost(service: NsdServiceInfo) {
        // When the network service is no longer available.
        // Internal bookkeeping code goes here.
        Log.e(TAG, "service lost: $service")
    }

    override fun onDiscoveryStopped(serviceType: String) {
        Log.i(TAG, "Discovery stopped: $serviceType")
    }

    override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
        Log.e(TAG, "Discovery failed: Error code:$errorCode")
        nsdManager.stopServiceDiscovery(this)
    }

    override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
        Log.e(TAG, "Discovery failed: Error code:$errorCode")
        nsdManager.stopServiceDiscovery(this)
    }
}

Java

public void initializeDiscoveryListener() {

    // Instantiate a new DiscoveryListener
    discoveryListener = new NsdManager.DiscoveryListener() {

        // Called as soon as service discovery begins.
        @Override
        public void onDiscoveryStarted(String regType) {
            Log.d(TAG, "Service discovery started");
        }

        @Override
        public void onServiceFound(NsdServiceInfo service) {
            // A service was found! Do something with it.
            Log.d(TAG, "Service discovery success" + 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(serviceName)) {
                // 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: " + serviceName);
            } else if (service.getServiceName().contains("NsdChat")){
                nsdManager.resolveService(service, resolveListener);
            }
        }

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

        @Override
        public void onStartDiscoveryFailed(String serviceType, int errorCode) {
            Log.e(TAG, "Discovery failed: Error code:" + errorCode);
            nsdManager.stopServiceDiscovery(this);
        }

        @Override
        public void onStopDiscoveryFailed(String serviceType, int errorCode) {
            Log.e(TAG, "Discovery failed: Error code:" + errorCode);
            nsdManager.stopServiceDiscovery(this);
        }
    };
}

L'API NSD utilizza i metodi in questa interfaccia per informare l'applicazione durante il rilevamento all'avvio, in caso di errore e quando i servizi vengono trovati e persi (la perdita significa non più disponibile"). Tieni presente che questo snippet esegue diversi controlli quando viene trovato un servizio.

  1. Il nome del servizio trovato viene confrontato con quello del servizio nome del servizio locale per determinare se il dispositivo ha appena rilevato broadcast (valido).
  2. Il tipo di servizio è selezionato, per verificare che si tratti di un tipo di servizio a cui può connettersi l'applicazione.
  3. Il nome del servizio viene controllato per verificare la connessione all'account di servizio corretto un'applicazione.

La verifica del nome del servizio non è sempre necessaria ed è pertinente solo se vuoi connetterti a un'applicazione specifica. Ad esempio, l'applicazione Vogliono connettersi solo a istanze di sé in esecuzione su altri dispositivi. Tuttavia, se connessione a una stampante di rete, è sufficiente vedere che il tipo di servizio è "_ipp._tcp".

Dopo aver configurato il listener, chiama discoverServices(), passando il tipo di servizio l'applicazione deve cercare, il protocollo di rilevamento da utilizzare e che hai appena creato.

Kotlin

nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener)

Java

nsdManager.discoverServices(
        SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener);

Connettiti ai servizi sulla rete

Quando l'applicazione trova sulla rete un servizio a cui connettersi, deve prima determinare le informazioni di connessione per tale servizio, utilizzando resolveService(). Implementa un NsdManager.ResolveListener da passare in questo e usalo per ottenere un NsdServiceInfo che contiene le informazioni di connessione.

Kotlin

private val resolveListener = object : NsdManager.ResolveListener {

    override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
        // Called when the resolve fails. Use the error code to debug.
        Log.e(TAG, "Resolve failed: $errorCode")
    }

    override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
        Log.e(TAG, "Resolve Succeeded. $serviceInfo")

        if (serviceInfo.serviceName == mServiceName) {
            Log.d(TAG, "Same IP.")
            return
        }
        mService = serviceInfo
        val port: Int = serviceInfo.port
        val host: InetAddress = serviceInfo.host
    }
}

Java

public void initializeResolveListener() {
    resolveListener = 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(serviceName)) {
                Log.d(TAG, "Same IP.");
                return;
            }
            mService = serviceInfo;
            int port = mService.getPort();
            InetAddress host = mService.getHost();
        }
    };
}

Una volta risolto il servizio, l'applicazione riceve informazioni dettagliate le informazioni sul servizio, tra cui l'indirizzo IP e il numero di porta. Questo è tutto devi creare la tua connessione di rete al servizio.

Annulla la registrazione del servizio alla chiusura dell'applicazione

È importante abilitare e disabilitare NSD le funzionalità più appropriate durante il deployment durante il ciclo di vita di attività. L'annullamento della registrazione dell'applicazione alla chiusura contribuisce a evitare altre applicazioni pensi che sia ancora attiva e tenta di connettersi li annotino. Inoltre, il Service Discovery è un'operazione costosa e deve essere arrestata quando l'attività principale viene messa in pausa e riattivata quando l'attività viene ripristinato. Esegui l'override dei metodi del ciclo di vita dell'attività principale e inserisci il codice per avviare e interrompere la trasmissione e il rilevamento del servizio in base alle esigenze.

Kotlin

    // In your application's Activity

    override fun onPause() {
        nsdHelper?.tearDown()
        super.onPause()
    }

    override fun onResume() {
        super.onResume()
        nsdHelper?.apply {
            registerService(connection.localPort)
            discoverServices()
        }
    }

    override fun onDestroy() {
        nsdHelper?.tearDown()
        connection.tearDown()
        super.onDestroy()
    }

    // NsdHelper's tearDown method
    fun tearDown() {
        nsdManager.apply {
            unregisterService(registrationListener)
            stopServiceDiscovery(discoveryListener)
        }
    }

Java

    // In your application's Activity

    @Override
    protected void onPause() {
        if (nsdHelper != null) {
            nsdHelper.tearDown();
        }
        super.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (nsdHelper != null) {
            nsdHelper.registerService(connection.getLocalPort());
            nsdHelper.discoverServices();
        }
    }

    @Override
    protected void onDestroy() {
        nsdHelper.tearDown();
        connection.tearDown();
        super.onDestroy();
    }

    // NsdHelper's tearDown method
    public void tearDown() {
        nsdManager.unregisterService(registrationListener);
        nsdManager.stopServiceDiscovery(discoveryListener);
    }