Android IntentService的蓝牙连接问题

Android IntentService的蓝牙连接问题,android,bluetooth,Android,Bluetooth,我需要通过蓝牙从我的Android应用程序传输一些数据(到Arduino)。我没有阅读/收到Arduino的任何回复。为了满足我的单线程需求,我使用了IntentService。配对后,我的代码在第一次连接和发送数据时工作正常。我在发送数据无误后断开连接。但是,当我第二次尝试连接时,在尝试myBluetoothSocket.connect()时出现以下错误: 读取失败,套接字可能已关闭或超时,读取重试:-1 唯一的解决方案是关闭Arduino设备的电源并重新连接(如果我强制停止应用程序并尝试重新

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

读取失败,套接字可能已关闭或超时,读取重试:-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)上进行了测试。

当您第二次尝试连接时,必须再次创建相应的插座

你也必须考虑ARDUNO是一个缓慢的平台,关闭连接可能会有相当大的延迟,你可以再次打开它。

< p> <代码>毁灭()>代码>方法只在垃圾回收器运行时调用。您需要像以前一样从
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;
    }

我觉得您的问题在于使用
IntentService
而不是常规
服务和/或自定义
线程。从
IntentService
doc:服务根据需要启动,使用工作线程依次处理每个Intent,并在其工作结束时停止。这可能会造成严重破坏,如果你试图维持一个BT连接的意图。这正是我需要的。连接、发送数据并终止连接。下一个连接可能会在很长时间后发生,从而产生一个新的服务。对于这样一个简单的用例,维护线程似乎很麻烦。这也自动确保了一次只能处理一台设备(我的使用案例的一部分)。你所说的Arduino没有“阻止”连接似乎是合理的,但你必须关闭和打开Arduino才能再次连接这一事实让我感到奇怪。如果你在同一个线程上读写,Arduino可能会进入不同的状态?(我知道,听起来很奇怪。)如果你用一部手机连接Arduino,断开连接,然后尝试用第二部手机连接,会发生什么?另外,你有没有办法查看Arduino的蓝牙调制解调器的状态,看看第一次断开后与之前相比是否有什么不同?在你提到的场景中,Arduino能够连接到第二部手机。我不知道如何检查调制解调器的状态。我还制作了两个应用程序(一个在主线程上执行所有操作,另一个使用IntentService)。如果我使用IntentService连接,则另一个应用程序(使用主线程)无法连接到同一个设备。我假设您已经看到我发布的代码每次都在创建一个新套接字。你不同意吗?