2015-05-16 16 views
8

Ich muss einige Daten von meiner Android App über Bluetooth (zu Arduino) übertragen. Ich lese/empfange nichts von Arduino zurück. Für meine Single-Thread-Bedürfnisse ging ich mit einem IntentService. Nach dem Pairing funktioniert mein Code zum ersten Mal, wenn ich Daten anschließe und sende. Ich trenne nach dem Senden von Daten ohne Fehler. Aber wenn ich versuche weiter, um das zweite Mal zu verbinden, ich die folgende Fehlermeldung erhalten, wenn ich versuche myBluetoothSocket.connect():Bluetooth Verbindungsprobleme mit IntentService

Lese fehlgeschlagen ist, Socket geschlossen oder Timeout könnte, lesen Sie ret: -1

Einzige Lösung ist, das Arduino-Gerät auszuschalten und die Verbindung wiederherzustellen (es hilft nicht, wenn ich zwinge, die App zu stoppen und eine erneute Verbindung herzustellen).

Beachten Sie, dass alles funktioniert, wenn ich 2 Threads spawnen (eines zum Lesen und Schreiben) unabhängig davon, wie oft ich verbinde und Daten sende (dadurch beweist, dass auf der Arduino-Seite nichts falsch ist, ein altes "zurückhalten") Verbindung).

Hier mein Android-Code ist:

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

ich auf Nexus 5 und Samsung S5 Geräte getestet (Lauf 5.1 und 5.0 beziehungsweise).

+0

Ich habe das Gefühl, Ihr Problem liegt eine 'IntentService' eher als ein normales' Service' und/oder kundenspezifische 'Thema bei der Verwendung '. Aus dem 'IntentService'-Dokument: * Der Dienst wird nach Bedarf gestartet, behandelt jede Absicht der Reihe nach mit einem Arbeitsthread und stoppt sich selbst, wenn er keine Arbeit mehr hat. * Dies wird wahrscheinlich Chaos verursachen, wenn Sie versuchen, ein BT zu verwalten Verbindung über Intents. – pathfinderelite

+0

Genau das brauche ich. Verbinden, Daten senden und Verbindung beenden. Die nächste Verbindung würde wahrscheinlich nach sehr langer Zeit stattfinden und einen neuen Dienst hervorbringen. Die Pflege von Threads scheint für einen einfachen Anwendungsfall wie diesen umständlich zu sein. Dies gewährleistet auch automatisch die Behandlung von jeweils einem Gerät (Teil meines Anwendungsfalls). – SlowAndSteady

+0

Was Sie über das Arduino nicht "zurückhalten" sagen, scheint die Verbindung vernünftig, aber die Tatsache, dass Sie den Arduino aus- und wieder einschalten müssen, um sich wieder verbinden zu können, lässt mich wundern. Vielleicht geht der Arduino irgendwie in einen anderen Zustand, wenn Sie auf demselben Thread lesen und schreiben? (Ich weiß, klingt seltsam.) Was passiert, wenn Sie mit einem Telefon mit dem Arduino verbinden, trennen, dann versuchen, mit dem zweiten Telefon zu verbinden? Haben Sie auch eine Möglichkeit, den Status des Arduino Bluetooth-Modems zu sehen, um zu sehen, ob es irgendwie anders ist, nachdem Sie das erste Mal trennen, im Vergleich zu vorher? – hBrent

Antwort

0

Ich bin nicht sicher, warum es funktioniert, aber dieser Ansatz arbeitete schließlich:

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

Wenn Sie versuchen, das zweite Mal eine Verbindung herzustellen, müssen Sie den entsprechenden Socket erneut erstellen.

Auch Sie müssen berücksichtigen, Arduino ist eine langsame Plattform, es könnte einige erhebliche Verzögerung zwischen dem Schließen der Verbindung und Sie in der Lage sein, es wieder zu öffnen.

+0

Der Code, den ich gepostet habe, erstellt einen neuen Socket jedes Mal. Stimmst du nicht zu? – SlowAndSteady

-1

Die Methode onDestroy() wird nur aufgerufen, wenn der Garbage Collector ausgeführt wird. Sie müssen die cleanup() von der onHandleIntent(Intent) wie zuvor anrufen, sonst bleibt der Socket unbegrenzt offen. Da Sie es offen gelassen haben, können Sie keine Verbindung mehr herstellen.

Der Bluetooth-Stack von Android scheint unabhängig vom Anwendungslebenszyklus zu sein: Der Sockel bleibt geöffnet, auch wenn Sie die Anwendung zwingen, die Anwendung zu beenden. In Ihrem aktuellen Szenario, um den Socket zu schließen, deaktivieren Sie Bluetooth in den Einstellungen.