2015-05-16 57 views
8

我需要通过蓝牙(到Arduino)从我的Android应用程序传输一些数据。我没有阅读/从Arduino接收任何东西。对于我的单线程需求,我使用了IntentService。配对后,我的代码在我第一次连接并发送数据时工作正常。发送数据后没有错误,我断开连接。但是,当我尝试第二次起连接,我收到以下错误,当我尝试myBluetoothSocket.connect():IntentService与蓝牙连接问题

读取失败,插座可能关闭或超时,阅读RET:-1

唯一的解决方案是关闭Arduino设备并重新连接(如果我强制停止应用程序并尝试重新连接,则无法解决问题)。

请注意,如果我产生2个线程(每个读写一个线程),无论连接和发送数据多少次,一切正常(从而证明Arduino方面没有任何问题,“阻止”一个旧的连接)。

这是我的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); 
     } 
    } 
} 

我已经在Nexus 5和三星S5装置上测试(分别为5.1运行和5.0)。

+0

我有一种感觉,你的问题就出在使用'IntentService'而不是一个常规'Service'和/或定制'主题'。从'IntentService'文档:*服务根据需要启动,使用工作线程处理每个Intent,并在工作失效时自行停止。*如果您尝试维护BT,这可能会造成严重破坏连接意图。 – pathfinderelite

+0

这正是我需要的。连接,发送数据并终止连接。下一次连接可能会在很长时间后发生,产生一项新服务。维护线程看起来像这样简单的用例很麻烦。这也可以自动确保一次处理一个设备(我的使用案例的一部分)。 – SlowAndSteady

+0

你说Arduino不“阻止”连接似乎是合理的,但事实是你必须关闭Arduino并能够再次连接才能让我感到惊讶。如果你在同一个线程上读写,Arduino可能会进入不同的状态? (我知道,听起来很奇怪。)如果你用一部手机连接到Arduino,断开连接,然后尝试连接第二部手机,会发生什么?另外,你有什么方法可以看到Arduino的蓝牙调制解调器的状态,看看它在第一次断开连接后是否有点不同,与之前相比? – hBrent

回答

0

我不知道为什么它的工作原理,但这种方法终于摸索:

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

当您尝试第二次连接时,必须再次创建相应的套接字。

此外,您必须考虑Arduino是一个缓慢的平台,关闭连接和您可以再次打开它之间可能会有一些相当大的延迟。

+0

我发布的代码是每次创建一个新的套接字。你不同意吗? – SlowAndSteady

-1

仅当垃圾收集器运行时调用onDestroy()方法。您需要拨打onHandleIntent(Intent)中的cleanup(),否则套接字将无限期地保持打开状态。由于您将其打开,因此无法再次连接。

Android的蓝牙堆栈似乎与应用程序生命周期无关:即使强制停止应用程序,套接字也将保持打开状态。在您当前的情况下,要关闭套接字,请在“设置”中禁用蓝牙。