IntentService 的蓝牙连接问题
Bluetooth connection issues with IntentService
我需要通过蓝牙从我的 Android 应用程序传输一些数据(到 Arduino)。我不是 reading/receiving 从 Arduino 回来的任何东西。对于我的单线程需求,我选择了 IntentService。配对后,我的代码在我第一次连接和发送数据时工作正常。我在无误发送数据后断开连接。但是当我第二次尝试连接时,尝试 myBluetoothSocket.connect() :
时出现以下错误
read failed, socket might closed or timeout, read ret: -1
唯一的解决办法是关闭 Arduino 设备电源并重新连接(如果我强制停止应用程序并尝试重新连接,这无济于事)。
请注意,无论我连接和发送数据多少次,如果我生成 2 个线程(每个线程一个用于读取和写入)一切正常(从而证明 Arduino 方面没有任何问题,"holding back"旧连接)。
这是我的 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 和 Samsung S5 设备上测试过(运行 5.1 和 5.0)。
当您第二次尝试连接时,您必须重新创建相应的套接字。
您还必须考虑到 Arduino 是一个缓慢的平台,在关闭连接和您能够再次打开它之间可能会有相当长的延迟。
onDestroy()
方法仅在垃圾收集器运行时调用。您需要像以前一样从 onHandleIntent(Intent)
调用 cleanup()
,否则套接字将无限期保持打开状态。由于您将其打开,因此无法再次连接。
Android 的蓝牙堆栈似乎与应用程序生命周期无关:即使您强行停止应用程序,套接字仍将保持打开状态。在您当前的情况下,要关闭套接字,请在“设置”中禁用/启用蓝牙。
我不确定它为什么有效,但这种方法终于奏效了:
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;
}
我需要通过蓝牙从我的 Android 应用程序传输一些数据(到 Arduino)。我不是 reading/receiving 从 Arduino 回来的任何东西。对于我的单线程需求,我选择了 IntentService。配对后,我的代码在我第一次连接和发送数据时工作正常。我在无误发送数据后断开连接。但是当我第二次尝试连接时,尝试 myBluetoothSocket.connect() :
时出现以下错误read failed, socket might closed or timeout, read ret: -1
唯一的解决办法是关闭 Arduino 设备电源并重新连接(如果我强制停止应用程序并尝试重新连接,这无济于事)。
请注意,无论我连接和发送数据多少次,如果我生成 2 个线程(每个线程一个用于读取和写入)一切正常(从而证明 Arduino 方面没有任何问题,"holding back"旧连接)。
这是我的 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 和 Samsung S5 设备上测试过(运行 5.1 和 5.0)。
当您第二次尝试连接时,您必须重新创建相应的套接字。
您还必须考虑到 Arduino 是一个缓慢的平台,在关闭连接和您能够再次打开它之间可能会有相当长的延迟。
onDestroy()
方法仅在垃圾收集器运行时调用。您需要像以前一样从 onHandleIntent(Intent)
调用 cleanup()
,否则套接字将无限期保持打开状态。由于您将其打开,因此无法再次连接。
Android 的蓝牙堆栈似乎与应用程序生命周期无关:即使您强行停止应用程序,套接字仍将保持打开状态。在您当前的情况下,要关闭套接字,请在“设置”中禁用/启用蓝牙。
我不确定它为什么有效,但这种方法终于奏效了:
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;
}