基于UUID去建立一条低速的无需配对的蓝牙通道
一、原理
蓝牙是一种使用无线电通信的技术完成设备与设备间通讯与数据交换。
UUID是什么:不同的服务用不同的UUID区分。
服务是什么:能提供的功能。
UUID的详细解释:俩中国人用汉语交流,汉语就是俩中国人的UUID,交流就是服务。蓝牙技术联盟SIG定义UUID共用了一个基本的UUID:0x0000xxxx-0000-1000-8000-00805F9B34FB。总共128位,为了进一步简化基本UUID,每一个蓝牙技术联盟定义的属性有一个唯一的16位UUID,以代替上面的基本UUID的‘x’部分。使用16位的UUID便于记忆和操作,如SIG定义了“Device Information”的16位UUID为0x180A。
补充:很多厂家其实都没有用标准的UUID,都是自己定义的。这样其实很混乱,你要基于UUID去创建通道,首先要保证,你的这个UUID能被遍历到。
蓝牙是一个很有趣的东西,这里贴一个蓝牙协议《bluetooth_Core_v4.2.pdf》下载地址(白嫖):
https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=286439
使用下面代码创建的蓝牙低速通道,实际上会创建一个ACL链路:
两个蓝牙设备的配对,先建立一条ACL通道。随后,会进行L2CAP的一些交互,获取信息,但需要基于SDP profile获取信息时,L2CAP就会建立一条SDP逻辑链路(由Channel ID标识,这个可以理解成socket通信的端口号),SDP的交互就在L2CAP建立的Channel上进行,SDP交互完成后,会断开SDP的逻辑链路,若后面没有其它再需要交互的,ACL链路也会断开,但由于已经配对过,上层会保存ACL配对的信息。
附一个简单的图解:
二、代码核心解释
server端就是通过原生API,listenUsingRfcommWithServiceRecord或者listenUsingInsecureRfcommWithServiceRecord来监听这个UUID,方便client来连。这俩方法就是安全/不安全的连接,可以去官网查看相关定义。
通过监听,当监听到之后,就返回一个BluetoothSocket对象,直接accept,等待client来connect(和普通socket一样,但是不存在子类父类关系哦~)
client端也是通过原生API,createRfcommSocketToServiceRecord或者createInsecureRfcommSocketToServiceRecord,根据UUID去连这个服务,同样返回的也是一个BluetoothSocket,然后基于这个socket去调用connect。
随后数据传输就是socket通信,无需赘述。
难点就是要理解,listenUsingRfcommWithServiceRecord和createRfcommSocketToServiceRecord,是蓝牙层面上的连接,并非是socket的连接,只有这个连接建立好了,再去搞socket连接,然后流通信。
三、代码
client端
package com.mythsun.frcommclient;
import androidx.appcompat.app.AppCompatActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.UUID;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "client-demo";
private Button button1, button2;
private TextView textView1;
private final BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
// use
private BluetoothSocket client;
private class ConnectThread extends Thread {
private final String UUID_STRING = "你自己的uuid";
private final UUID UUID_BR = UUID.fromString(UUID_STRING);
private final boolean isSecure;
public ConnectThread(BluetoothDevice device, boolean isSecure) {
this.isSecure = isSecure;
BluetoothSocket tmp = null;
try {
if (this.isSecure) {
tmp = device.createRfcommSocketToServiceRecord(UUID_BR);
Log.d(TAG, "isSecure:true");
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(UUID_BR);
Log.d(TAG, "isSecure:false");