Conectar dispositivo Bluetooth

Para criar uma conexão entre dois dispositivos, você deve implementar o mecanismos do lado do servidor e do cliente, porque um dispositivo deve abrir um servidor e o outro deve iniciar a conexão usando o endereço MAC. O dispositivo do servidor e o dispositivo do cliente recebem as solicitações BluetoothSocket em diferentes de várias formas. O servidor recebe informações do soquete quando uma conexão de entrada é aceita. O cliente fornece informações do soquete quando abre um canal RFCOMM ao servidor.

O servidor e o cliente são considerados conectados entre si quando cada um tem um BluetoothSocket conectado no mesmo canal RFCOMM. Neste ponto, cada dispositivo pode receber fluxos de entrada e saída, e a transferência de dados pode começar, o que é discutido na seção sobre como transferir o Bluetooth dados. Esta seção descreve como iniciar a conexão entre dois dispositivos.

Verifique se você tem Permissões de Bluetooth e configure o app para Bluetooth antes tentando encontrar dispositivos Bluetooth.

Técnicas de conexão

Uma técnica de implementação é preparar automaticamente cada dispositivo como servidor para que cada dispositivo tenha um soquete de servidor aberto e detectando conexões. Em neste caso, qualquer um dos dispositivos pode iniciar uma conexão com o outro e se tornar o para o cliente. Como alternativa, um dispositivo pode hospedar explicitamente a conexão e abrir um soquete do servidor de anúncios sob demanda, e o outro dispositivo inicia a conexão.


Figura 1. Caixa de diálogo de pareamento do Bluetooth.

Conectar como servidor

Para conectar dois dispositivos, um deve atuar como servidor segurando uma abrem BluetoothServerSocket O objetivo do soquete do servidor é detectar as solicitações de conexão de entrada e forneça um BluetoothSocket conectado depois que uma solicitação for aceita. Quando o O BluetoothSocket é adquirido do BluetoothServerSocket, o BluetoothServerSocket pode e deve ser descartado, a menos que você queira o dispositivo aceite mais conexões.

Para configurar um soquete de servidor e aceitar uma conexão, faça o seguinte sequência de etapas:

  1. Para receber um BluetoothServerSocket, faça uma chamada listenUsingRfcommWithServiceRecord(String, UUID).

    A string é um nome identificável do seu serviço, que o sistema grava automaticamente em uma nova entrada de banco de dados do Service Discovery Protocol (SDP) no dispositivo. O nome é arbitrário e pode ser simplesmente o nome do seu app. O identificador universal exclusivo (UUID) também está incluído na entrada SDP e forma a base para o acordo de conexão com o dispositivo cliente. Isso quando o cliente tenta se conectar a esse dispositivo, ele carrega um UUID que identifica de forma exclusiva o serviço ao qual deseja se conectar. Esses Os UUIDs precisam corresponder para que a conexão seja aceita.

    Um UUID é um formato padronizado de 128 bits para um ID de string usado para e identificar informações. Um UUID é usado para identificar informações que precisam ser único em um sistema ou rede, porque a probabilidade de um UUID ser repetidos é efetivamente zero. Ele é gerado de modo independente, sem o uso de uma autoridade centralizada. Nesse caso, ele é usado para identificar exclusivamente seu para o serviço Bluetooth desse app. Para receber um UUID para uso com seu app, use um das muitas opções aleatórias UUID na Web e, em seguida, inicialize um UUID com fromString(String)

  2. Para começar a detectar solicitações de conexão, chame accept()

    Essa é uma chamada de bloqueio. Ela é retornada quando uma conexão aceito ou que tenha ocorrido uma exceção. Uma conexão é aceita somente quando um dispositivo remoto enviou uma solicitação de conexão contendo um UUID que corresponde aquele registrado nesse soquete do servidor de detecção. Quando bem-sucedido, accept() retorna um BluetoothSocket conectado.

  3. A menos que você queira aceitar outras conexões, chame close()

    Essa chamada de método libera o soquete do servidor e todos os seus recursos, mas não fecha o BluetoothSocket conectado retornado pelo accept(). Ao contrário do TCP/IP, o RFCOMM permite apenas um cliente conectado por canal por vez. Por isso, na maioria dos casos, faz sentido chamar close() no BluetoothServerSocket imediatamente após aceitar um soquete conectado.

Como a chamada accept() é de bloqueio, não a execute na instância principal linha de execução de interface de atividade. A execução em outra linha de execução garante que seu app possa ainda respondem a outras interações do usuário. Geralmente faz sentido fazer todo o trabalho que envolve um BluetoothServerSocket ou BluetoothSocket em uma nova linha de execução gerenciados pelo seu app. Para cancelar uma chamada bloqueada, como accept(), chame close(). na BluetoothServerSocket ou BluetoothSocket de outra linha de execução. Observação que todos os métodos em uma BluetoothServerSocket ou BluetoothSocket seguro para linhas de execução.

Exemplo

Veja a seguir um thread simplificado para o componente do servidor que aceita conexões de entrada:

Kotlin

private inner class AcceptThread : Thread() {

   private val mmServerSocket: BluetoothServerSocket? by lazy(LazyThreadSafetyMode.NONE) {
       bluetoothAdapter?.listenUsingInsecureRfcommWithServiceRecord(NAME, MY_UUID)
   }

   override fun run() {
       // Keep listening until exception occurs or a socket is returned.
       var shouldLoop = true
       while (shouldLoop) {
           val socket: BluetoothSocket? = try {
               mmServerSocket?.accept()
           } catch (e: IOException) {
               Log.e(TAG, "Socket's accept() method failed", e)
               shouldLoop = false
               null
           }
           socket?.also {
               manageMyConnectedSocket(it)
               mmServerSocket?.close()
               shouldLoop = false
           }
       }
   }

   // Closes the connect socket and causes the thread to finish.
   fun cancel() {
       try {
           mmServerSocket?.close()
       } catch (e: IOException) {
           Log.e(TAG, "Could not close the connect socket", e)
       }
   }
}

Java

private class AcceptThread extends Thread {
   private final BluetoothServerSocket mmServerSocket;

   public AcceptThread() {
       // Use a temporary object that is later assigned to mmServerSocket
       // because mmServerSocket is final.
       BluetoothServerSocket tmp = null;
       try {
           // MY_UUID is the app's UUID string, also used by the client code.
           tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
       } catch (IOException e) {
           Log.e(TAG, "Socket's listen() method failed", e);
       }
       mmServerSocket = tmp;
   }

   public void run() {
       BluetoothSocket socket = null;
       // Keep listening until exception occurs or a socket is returned.
       while (true) {
           try {
               socket = mmServerSocket.accept();
           } catch (IOException e) {
               Log.e(TAG, "Socket's accept() method failed", e);
               break;
           }

           if (socket != null) {
               // A connection was accepted. Perform work associated with
               // the connection in a separate thread.
               manageMyConnectedSocket(socket);
               mmServerSocket.close();
               break;
           }
       }
   }

   // Closes the connect socket and causes the thread to finish.
   public void cancel() {
       try {
           mmServerSocket.close();
       } catch (IOException e) {
           Log.e(TAG, "Could not close the connect socket", e);
       }
   }
}

Neste exemplo, é desejada apenas uma conexão de entrada, portanto, a conexão for aceita e o BluetoothSocket for adquirido, o app vai transmitir o Adquiriu BluetoothSocket para uma linha de execução separada, fecha o BluetoothServerSocket e sai da repetição.

Quando accept() retornar o BluetoothSocket, o soquete já estará conectados. Portanto, não chame connect(), da mesma forma que você do lado do cliente.

O método manageMyConnectedSocket() específico do app foi projetado para iniciar a de transferência de dados, que é discutido no tópico sobre transferindo Bluetooth dados.

Normalmente, é necessário fechar o BluetoothServerSocket ao terminar para detectar conexões de entrada. Neste exemplo, close() é chamado assim que quando o BluetoothSocket é adquirido. Você também pode fornecer um na linha de execução que pode fechar o BluetoothSocket particular no caso de que você precisa parar de detectar no soquete do servidor.

Conectar como cliente

Para iniciar uma conexão com um dispositivo remoto que está aceitando conexões em um soquete de servidor aberto, é preciso primeiro conseguir um BluetoothDevice que representa o dispositivo remoto. Para aprender a criar um BluetoothDevice, consulte Encontrar o Bluetooth dispositivos. Você deve Em seguida, use o BluetoothDevice para adquirir um BluetoothSocket e iniciar o uma conexão com a Internet.

Este é o procedimento básico:

  1. Usando o BluetoothDevice, receba um BluetoothSocket chamando createRfcommSocketToServiceRecord(UUID).

    Esse método inicializa um objeto BluetoothSocket que permite ao cliente conectar a um BluetoothDevice. O UUID transmitido aqui precisa corresponder ao UUID usado pelo dispositivo do servidor quando chamou listenUsingRfcommWithServiceRecord(String, UUID) para abrir o BluetoothServerSocket. Para usar um UUID correspondente, codifique o string UUID no seu app e referencie-a no servidor e o código do cliente.

  2. Inicie a conexão chamando connect(). Esse método é uma chamada de bloqueio.

    Depois que um cliente chama esse método, o sistema realiza uma pesquisa SDP para encontrar o dispositivo remoto com o UUID correspondente. Se a pesquisa for bem-sucedida e o dispositivo remoto aceita a conexão, compartilha o canal RFCOMM para uso durante a conexão, e o método connect() é retornado. Se a conexão falhar, ou se o método connect() expirar (após cerca de 12 segundos), o método gera uma IOException.

Como connect() é uma chamada de bloqueio, sempre realize essa ação procedimento de conexão em uma linha de execução separada da atividade principal (IU) fio

Exemplo

Este é um exemplo básico de um thread de cliente que inicia uma solicitação conexão:

Kotlin

private inner class ConnectThread(device: BluetoothDevice) : Thread() {

   private val mmSocket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) {
       device.createRfcommSocketToServiceRecord(MY_UUID)
   }

   public override fun run() {
       // Cancel discovery because it otherwise slows down the connection.
       bluetoothAdapter?.cancelDiscovery()

       mmSocket?.let { socket ->
           // Connect to the remote device through the socket. This call blocks
           // until it succeeds or throws an exception.
           socket.connect()

           // The connection attempt succeeded. Perform work associated with
           // the connection in a separate thread.
           manageMyConnectedSocket(socket)
       }
   }

   // Closes the client socket and causes the thread to finish.
   fun cancel() {
       try {
           mmSocket?.close()
       } catch (e: IOException) {
           Log.e(TAG, "Could not close the client socket", e)
       }
   }
}

Java

private class ConnectThread extends Thread {
   private final BluetoothSocket mmSocket;
   private final BluetoothDevice mmDevice;

   public ConnectThread(BluetoothDevice device) {
       // Use a temporary object that is later assigned to mmSocket
       // because mmSocket is final.
       BluetoothSocket tmp = null;
       mmDevice = device;

       try {
           // Get a BluetoothSocket to connect with the given BluetoothDevice.
           // MY_UUID is the app's UUID string, also used in the server code.
           tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
       } catch (IOException e) {
           Log.e(TAG, "Socket's create() method failed", e);
       }
       mmSocket = tmp;
   }

   public void run() {
       // Cancel discovery because it otherwise slows down the connection.
       bluetoothAdapter.cancelDiscovery();

       try {
           // Connect to the remote device through the socket. This call blocks
           // until it succeeds or throws an exception.
           mmSocket.connect();
       } catch (IOException connectException) {
           // Unable to connect; close the socket and return.
           try {
               mmSocket.close();
           } catch (IOException closeException) {
               Log.e(TAG, "Could not close the client socket", closeException);
           }
           return;
       }

       // The connection attempt succeeded. Perform work associated with
       // the connection in a separate thread.
       manageMyConnectedSocket(mmSocket);
   }

   // Closes the client socket and causes the thread to finish.
   public void cancel() {
       try {
           mmSocket.close();
       } catch (IOException e) {
           Log.e(TAG, "Could not close the client socket", e);
       }
   }
}

Neste snippet, o método cancelDiscovery() é chamado antes da conexão após uma tentativa de ataque. Sempre chame cancelDiscovery() antes de connect(). especialmente porque cancelDiscovery() funciona, independentemente de o dispositivo a descoberta está em andamento. Caso seu app precise determinar se a descoberta de dispositivos está em andamento. Você pode verificar isso usando isDiscovering()

O método manageMyConnectedSocket() específico do app foi projetado para iniciar a para transferir dados, que é discutido na seção sobre transferência de dados Bluetooth.

Quando terminar de usar o BluetoothSocket, sempre chame close(). Ao fazer isso fecha imediatamente o soquete conectado e libera todas as informações internas do Google Cloud.