Android蓝牙通用数据传输之一(SPP)


 

一、引言

Android蓝牙中涉及通用数据传输协议的有两种:

  • SPP协议
  • BLE(Bluetooth low energy)协议

SPP协议是Android 2.0引入的API,是通过Socket的形式来实现数据传输及交互,有分客户端和服务端,手机一般以客户端的角色主动连接SPP协议设备。
BLE协议是Android 4.3引入的API,但手机厂商大部份在Android 4.4上才支持BLE,即低功耗蓝牙,一般我们开发的话是使用中央(BluetoothGatt)或者外围(BluetoothGattServer)来进行开发的,手机正常情况下当作中央设备来接收信息,而蓝牙模块当作是外围设备发送数据。

二、SPP协议

SPP协议的连接流程:

  • 使用registerReceiver注册BroadcastReceiver来获取蓝牙状态、搜索设备等消息
  • 在BroadcastReceiver的onReceive()里取得搜索所得的蓝牙设备信息(如名称,MAC,RSSI)
  • 通过设备的MAC地址来建立一个BluetoothDevice对象
  • 由BluetoothDevice衍生出BluetoothSocket,准备SOCKET来读写设备
  • 通过BluetoothSocket的createRfcommSocketToServiceRecord()方法来选择连接的协议/服务,默认是SPP(UUID:00001101-0000-1000-8000-00805F9B34FB)
  • Connect之后(如果还没配对则系统自动提示),使用BluetoothSocket的getInputStream()和getOutputStream()来读写蓝牙设备
  • 关闭数据接口

SPP协议涉及4个类:

  • BluetoothAdapter:代表本地蓝牙适配器,是所有蓝牙交互的入口。使用这个你可以发现其他蓝牙设备,查询已配对的设备列表,使用一个已知的MAC地址来实例化一个BluetoothDevice,以及创建一个BluetoothServerSocket来为监听与其他设备的通信
  • BluetoothDevice:代表一个远程蓝牙设备,使用这个来请求一个与远程设备的BluetoothSocket连接,或者查询关于设备名称、地址、类和连接状态等设备信息
  • BluetoothSocket:代表一个蓝牙socket的接口(和TCP Socket类似)。这是一个连接点,它允许一个应用与其他蓝牙设备通过InputStream和OutputStream交换数据
  • BluetoothServerSocket:代表一个开放的服务器socket,它监听接受的请求(与TCP ServerSocket类似)。为了连接两台Android设备,一个设备必须使用这个类开启一个服务器socket。当一个远程蓝牙设备开始一个和该设备的连接请求,BluetoothServerSocket将会返回一个已连接的BluetoothSocket,接受该连接

获取本地蓝牙适配器:

    public static BluetoothAdapter getBtAdapter() {
        Context mContext = CSmartApplication.getInstance().getApplicationContext();
        BluetoothManager bluetoothManager = (BluetoothManager) mContext
                .getSystemService(Context.BLUETOOTH_SERVICE);
        return bluetoothManager.getAdapter();
    }

注册监听:

    private void requestProfileConnectionState() {
        //检测连接状态:
        int a2dp = mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.A2DP);
        int gatt = mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.GATT);
        int sap = mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.SAP);
        //据是否有连接获取已连接的设备
        int flag = -1;
        if (a2dp == BluetoothProfile.STATE_CONNECTED) {
            flag = a2dp;
        } else if (gatt == BluetoothProfile.STATE_CONNECTED) {
            flag = gatt;
        } else if (sap == BluetoothProfile.STATE_CONNECTED) {
            flag = sap;
        }
        if (flag != -1) {
            ProxyListener mProxyListener = new ProxyListener();
            mBluetoothAdapter.getProfileProxy(this, mProxyListener, flag);
        }
    }

    private void listenBlueState() {
        //检查当前是否存在蓝牙连接
        requestProfileConnectionState();
        if (mFondDevieReceiver == null) {
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
            intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
            mFondDevieReceiver = new FoundDeviceReceiver();
            registerReceiver(mFondDevieReceiver, intentFilter);
        }
    }


获取远程蓝牙设备:

 private class ProxyListener implements BluetoothProfile.ServiceListener {

        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {

            if (proxy != null) {
                List<BluetoothDevice> mDevices = proxy.getConnectedDevices();
                if (mDevices.size() > 0) {
                    for (int i = 0; i < mDevices.size(); i++) {
                        //获取BluetoothDevice
                        mDevice = mDevices.get(0);
                        logShow("mDevice: " + mDevices.get(0).getAddress());
                        //调用创建Socket连接
                        connectDevice();
                    }
                }
                mBluetoothAdapter.closeProfileProxy(profile, proxy);
            }
        }

        @Override
        public void onServiceDisconnected(int profile) {

        }
    }


    private class FoundDeviceReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)
                    || BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {

                int a2dpState = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1);
                int adapterState = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, BluetoothAdapter.ERROR);
                if (BluetoothA2dp.STATE_CONNECTED == a2dpState || BluetoothAdapter.STATE_CONNECTED == adapterState) {//连接成功
                    //获取BluetoothDevice
                    mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    logShow("STATE_CONNECTED: " + mDevice.getAddress());
                    //调用创建Socket连接
                    connectDevice();
                } else if (BluetoothA2dp.STATE_CONNECTING == a2dpState) {//正在连接
                } else if (BluetoothA2dp.STATE_DISCONNECTED == a2dpState) {//取消连接
                    logShow("STATE_DISCONNECTED");
                    ......
                }
            }
            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
                        BluetoothAdapter.ERROR);
                switch (state) {
                    case BluetoothAdapter.STATE_OFF:
                        break;
                    case BluetoothAdapter.STATE_TURNING_OFF:
                        break;
                    case BluetoothAdapter.STATE_ON:
                        break;
                    case BluetoothAdapter.STATE_TURNING_ON:
                        break;
                }
            }
        }
    }

创建Socket:

创建RFCOMM的UUID需要与设备端约定,一般使用默认的就可以,但有时因项目特殊,也会另有定义。

 private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");//默认UUID
    /**
     * Create the RFCOMM bluetooth socket.
     *
     * @param uuid UUID to create the socket with.
     * @return BluetoothSocket object.
     */
    @TargetApi(10)
    private BluetoothSocket createSocket(UUID uuid) {
        BluetoothSocket socket = null;
        try {
            if (VERSION.SDK_INT >= 10) {
                socket = mBTDevice.createInsecureRfcommSocketToServiceRecord(uuid);
            } else {
                socket = mBTDevice.createRfcommSocketToServiceRecord(uuid);
            }
        } catch (IOException e) {
            if (mDebug)
                Log.e(TAG, "createSocket: " + e.toString());
            try {
                Method method = mBTDevice.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
                socket = (BluetoothSocket) method.invoke(mBTDevice, Integer.valueOf(1));
                return socket;
            } catch (Exception e1) {
                if (mDebug)
                    handleException("createSocket", TypeException.CONNECTION_FAILED, e1);
            }
        }

        return socket;
    }

数据读写:

注:mInputStream.read(buffer)是I/O口堵塞式的

    mBTSocket.connect();
    mInputStream = mBTSocket.getInputStream();

    //启动新线程去处理
    while (this.going) {
        if (mInputStream != null) {
            try {
                int bytes = mInputStream.read(buffer);
                if (bytes < 0) {
                    this.going = false;
                } else {
                    Log.e(TAG, "buffer: " + Arrays.toString(buffer));
                    //数据解析
                    mHandler.obtainMessage(Message.MESSAGE_READ, bytes, -1, buffer)
                        .sendToTarget();
                }
            } catch (Exception e) {
                Log.e(TAG, "mInputStream read is null");
                this.going = false;
            }
        }
    }
    public void sendData(byte[] data) {
        if (!this.mDfuBusy) {//判断连接是否有效
            if (mBTSocket == null) {
                Log.e(TAG, "sendCommandData: not connected.");
                return;
            }
            try {
                OutputStream outputStream = mBTSocket.getOutputStream();
                if (outputStream != null) {
                    try {
                        outputStream.write(data);
                    } catch (Exception e) {
                        Log.e(TAG, "outputStream.write null");
                    }
                } else {
                    Log.e(TAG, "outputStream is null");
                }
            } catch (IOException e) {
                Log.e(TAG, "sendData: " + e);
            }
        }
    }

接口关闭:

    private void disconnectBluetooth() {
        if (mDebug) {
            Log.i(TAG, "disconnect BT");
        }
        if (mBTSocket != null) {
            try {
                if (mInputStream != null) {
                    mInputStream.close();
                    mInputStream = null;
                }
                if (mBTSocket != null) {
                    if (mBTSocket.getInputStream() != null) {
                        mBTSocket.getInputStream().close();
                    }
                }
                if (mBTSocket != null) {
                    if (mBTSocket.getOutputStream() != null) {
                        mBTSocket.getOutputStream().close();
                    }
                }
                if (mBTSocket != null) {
                    mBTSocket.close();
                }
                mBTSocket = null;
                mBTDevice = null;
            } catch (IOException e) {
                if (mDebug) {
                    Log.e(TAG, e.toString());
                }
            }
        }
    }

服务端的Socket建立与客户端差不多,需注意以下两个步骤:

  • 创建监听listen(蓝牙没有此监听,但需要通过whlie(true)死循环来一直监听的)
  • 通过accept(),如果有客户端连接,会创建一个新的Socket,体现出并发性,可以同时与多个socket通讯)

总之:SPP的连接和操作相对比较简单,考虑的事情也少。需要注意的是频繁数据发送(间隔时间短)的情况下,APP收到的数据并不是按原先约定的TLV一包一包数据,可能会被拆包,应对的办法就是启动线程接收完数据,然后去一包一包取。


参考

  • 7
    点赞
  • 93
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
当然,我可以为您提供一个简单的示例代码来介绍如何在Android中使用蓝牙进行连接和数据传输。以下是一个基本的蓝牙连接和数据传输的代码示例: 1. 添加权限到AndroidManifest.xml文件中: ```xml <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> ``` 2. 在Activity中初始化蓝牙适配器和相关变量: ```java private BluetoothAdapter bluetoothAdapter; private BluetoothDevice bluetoothDevice; private BluetoothSocket bluetoothSocket; private OutputStream outputStream; private InputStream inputStream; // 初始化蓝牙适配器 bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); ``` 3. 检查设备是否支持蓝牙: ```java if (bluetoothAdapter == null) { // 设备不支持蓝牙 return; } ``` 4. 打开蓝牙: ```java if (!bluetoothAdapter.isEnabled()) { Intent enableBluetoothIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBluetoothIntent, REQUEST_ENABLE_BLUETOOTH); } ``` 5. 搜索并连接蓝牙设备: ```java // 搜索蓝牙设备 bluetoothAdapter.startDiscovery(); // 监听搜索到的蓝牙设备 BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // 根据设备名称或地址判断是否为目标设备 if (device.getName().equals("目标设备名称") || device.getAddress().equals("目标设备地址")) { bluetoothDevice = device; bluetoothAdapter.cancelDiscovery(); // 连接蓝牙设备 connectToDevice(); } } } }; IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(bluetoothReceiver, filter); ``` 6. 连接到蓝牙设备: ```java private void connectToDevice() { UUID uuid = UUID.fromString("00001101-1000-8000-00805F9B34FB"); // SPP通信协议的UUID try { bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(uuid); bluetoothSocket.connect(); outputStream = bluetoothSocket.getOutputStream(); inputStream = bluetoothSocket.getInputStream(); } catch (IOException e) { e.printStackTrace(); } } ``` 7. 发送数据: ```java String message = "Hello, Bluetooth!"; try { outputStream.write(message.getBytes()); } catch (IOException e) { e.printStackTrace(); } ``` 8. 接收数据: ```java byte[] buffer = new byte[1024]; int bytes; try { bytes = inputStream.read(buffer); String receivedMessage = new String(buffer, 0, bytes); } catch (IOException e) { e.printStackTrace(); } ``` 这是一个简单的蓝牙连接和数据传输的示例代码。您可以根据您的具体需求进行修改和扩展。如果您有任何进一步的问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值