2015-05-16 11 views
8

Devo trasmettere alcuni dati dalla mia app Android tramite Bluetooth (ad Arduino). Non sto leggendo/ricevendo nulla da Arduino. Per le mie esigenze single threaded, sono andato con un IntentService. Dopo l'accoppiamento, il mio codice funziona correttamente la prima volta che mi collego e inviamo i dati. Mi disconnetto dopo aver inviato dati senza errori. Ma quando provo a collegare la seconda volta in poi, ottengo il seguente errore quando provo myBluetoothSocket.connect():Problemi di connessione Bluetooth con IntentService

lettura non è riuscita, presa potrebbe chiusa o timeout, leggi ret: -1

L'unica soluzione è spegnere il dispositivo Arduino e riconnettersi (non è d'aiuto se faccio forza per fermare l'app e provare a ricollegarla).

Si noti che tutto funziona correttamente se si generano 2 thread (uno per leggere e scrivere ciascuno) indipendentemente dal numero di volte in cui mi collego e inviamo dati (provando quindi che non c'è nulla di sbagliato sul lato Arduino, "trattenere" un vecchio connessione).

Ecco il mio codice Android:

import android.app.IntentService; 
import android.bluetooth.BluetoothAdapter; 
import android.bluetooth.BluetoothDevice; 
import android.bluetooth.BluetoothSocket; 
import android.content.Intent; 
import android.content.Context; 
import android.os.Build; 
import android.os.ParcelUuid; 
import android.widget.Toast; 

import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.lang.reflect.Method; 
import java.util.UUID; 

public class DataTransmissionService extends IntentService { 

    private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); 
    private static final String TAG = "DataTransmissionService"; 

    private BluetoothAdapter btAdapter = null; 
    private BluetoothSocket btSocket = null; 
    private OutputStream outStream = null; 
    private BluetoothDevice device = null; 

    public DataTransmissionService() { 
     super("DataTransmissionService"); 
    } 

    @Override 
    protected void onHandleIntent(Intent intent) { 
     cleanup(); 
     if (intent != null){ 

      btAdapter = BluetoothAdapter.getDefaultAdapter(); 
      pairedDeviceAddress = "already_paired_device_mac_addr"; 

      try { 
       log.d(TAG, pairedDeviceAddress); 
       device = btAdapter.getRemoteDevice(pairedDeviceAddress); 
       log.d(TAG, "Device bond state : " + device.getBondState()); 

      } catch (Exception e) { 
       log.e(TAG, "Invalid address: " + e.getMessage()); 
       return; 
      } 

      try { 
       btSocket = createBluetoothSocket(device); 
      } catch (IOException e) { 
       log.e(TAG, "Socket creation failed: " + e.getMessage()); 
       return; 
      } 

      try { 

       if (!btSocket.isConnected()) { 
        btSocket.connect();  
        log.d(TAG, "Connected"); 
       } else { 
        log.d(TAG, "Already Connected"); //flow never reaches here for any use case 
       } 

      } catch (IOException e) { 
       log.e(TAG, "btSocket.connect() failed : " + e.getMessage()); 
       return; 
      } 

      try { 
       outStream = btSocket.getOutputStream(); 
      } catch (IOException e) { 
       log.e(TAG, "Failed to get output stream:" + e.getMessage()); 
       return; 
      } 

      sendData("test"); 
      //cleanup(); called in onDestroy() 

     } 

    } 

    @Override 
    public void onDestroy(){ 
     cleanup(); 
     //notify ui 
     super.onDestroy(); 
    } 

    private void cleanup(){ 

     try { 
      if (outStream != null) { 
       outStream.close(); 
       outStream = null; 
      } 
     } catch (Exception e) { 
      log.e(TAG, "Failed to close output stream : " + e.getMessage()); 
     } 

     try { 
      if (btSocket != null) { 
       btSocket.close(); 
       btSocket = null; 
      } 
     }catch (Exception e) { 
      log.e(TAG, "Failed to close connection : " + e.getMessage()); 
     } 

    } 

    private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException { 
     /*if(Build.VERSION.SDK_INT >= 10){ 
      try { 
       final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class }); 
       return (BluetoothSocket) m.invoke(device, MY_UUID); 
      } catch (Exception e) { 
       log.e(TAG, "Could not create Insecure RFComm Connection",e); 
      } 
     }*/ 

     return device.createRfcommSocketToServiceRecord(MY_UUID); 
    } 

    private void sendData(String message) { 

     byte[] msgBuffer = message.getBytes(); 
     log.d(TAG, "Sending : " + message); 
     try { 
      outStream.write(msgBuffer); 
     } catch (IOException e) { 
      log.e(TAG, "failed to write " + message); 
     } 
    } 
} 

Ho testato su Nexus 5 e dispositivi Samsung S5 (in esecuzione 5.1 e 5.0, rispettivamente).

+0

ho la sensazione vostro problema risiede nell'uso di un 'IntentService' piuttosto che un normale' Service' e/o personalizzati 'Discussione '. Dal documento 'IntentService': * il servizio viene avviato secondo necessità, gestisce ogni Intent a sua volta utilizzando un thread di lavoro e si arresta automaticamente quando termina il lavoro. * Questo potrebbe causare il caos se si sta tentando di mantenere un BT connessione attraverso intenti. – pathfinderelite

+0

Questo è esattamente ciò di cui ho bisogno. Connetti, invia dati e termina la connessione. La prossima connessione probabilmente accadrà dopo molto tempo, generando un nuovo servizio. Mantenere le discussioni sembra complicato per un semplice caso d'uso come questo. Ciò garantisce anche il trattamento automatico di un dispositivo alla volta (parte del mio caso d'uso). – SlowAndSteady

+0

Quello che dici di Arduino non "trattiene" la connessione sembra ragionevole, ma il fatto che tu debba spegnere e riaccendere l'Arduino per potermi connettere di nuovo mi fa meravigliare. Forse l'Arduino va in qualche modo in uno stato diverso se leggi e scrivi sullo stesso thread? (Lo so, sembra strano). Cosa succede se ti connetti ad Arduino con un telefono, disconnetti, quindi prova a connetterti con il secondo telefono? Inoltre, hai modo di vedere lo stato del modem bluetooth di Arduino, per vedere se è in qualche modo diverso dopo averlo disconnesso la prima volta, rispetto a prima? – hBrent

risposta

0

Io non sono sicuro perché funziona, ma questo approccio finalmente lavorato:

private BluetoothSocket createBluetoothSocket(BluetoothDevice bluetoothDevice) throws IOException { 

     try { 
      Method m = bluetoothDevice.getClass().getMethod(
        "createRfcommSocket", new Class[] { int.class }); 
      btSocket = (BluetoothSocket) m.invoke(bluetoothDevice, 1); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     return btSocket; 
    } 
2

Quando si tenta di connettersi la seconda volta è necessario creare nuovamente il socket corrispondente.

Inoltre, è necessario considerare che Arduino è una piattaforma lenta, potrebbe esserci un considerevole ritardo tra la chiusura della connessione e la possibilità di riaprirla.

+0

Il codice che ho postato crea ogni volta un nuovo socket. Non sei d'accordo? – SlowAndSteady

-1

Il metodo onDestroy() viene chiamato solo quando viene eseguito il garbage collector. È necessario chiamare lo cleanup() dal onHandleIntent(Intent) come prima, altrimenti il ​​socket resterà aperto indefinitamente. Dal momento che hai lasciato aperto, non riesci a connetterti di nuovo.

Lo stack Bluetooth di Android sembra essere agnostico del ciclo di vita dell'applicazione: il socket rimane aperto anche se si forza l'applicazione. Nel tuo scenario attuale, per chiudere il socket fai una disabilitazione del Bluetooth nelle Impostazioni.

Problemi correlati