虽然做开发有一段时间了,但是有关蓝牙方面一直接触的不多,正好目前有一个关于蓝牙开发的需求,虽然是用到的BLE和普通蓝牙有些区别,但是既然学习了就顺便把普通蓝牙也学习一下,也为了以后自己少走些弯路,先将这些学习笔记学习下来。官方文档
本例的源码已经上传,查看源码
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
蓝牙有关的所有的类如下:
普通蓝牙用到的几个关键的类:
BluetoothAdapter 蓝牙适配器
Represents the local device Bluetooth adapter. The BluetoothAdapter lets you perform fundamental Bluetooth tasks, such as initiate device discovery,
query a list of bonded (paired) devices, instantiate a BluetoothDevice using a known MAC address, and create a BluetoothServerSocket to listen for
connection requests from other devices, and start a scan for Bluetooth LE devices.
根据官方文档的说明,BluetoothAdapter
可以执行基本的蓝牙任务,比如启动设备发现,查询已配对的蓝牙列表,使用已知的地址实例化一个BluetoothDevice
,创建一个BluetoothServerSocket
监听链接请求等。
BluetoothDevice 蓝牙设备
Represents a remote Bluetooth device. A BluetoothDevice lets you create a connection with the respective device or query information about it,
such as the name, address, class, and bonding state.
代表一个远程蓝牙设备。BluetoothDevice
允许创建一个连接的设备或查询相关信息,如名称、地址、阶级和配对的状态。
BluetoothSocket
The interface for Bluetooth Sockets is similar to that of TCP sockets: Socket and ServerSocket.
顾名思义蓝牙连接
BluetoothServerSocket
The interface for Bluetooth Sockets is similar to that of TCP sockets: Socket and ServerSocket.
蓝牙服务链接,后两者都是和TCP端口类似的一个socket
。
首先打开蓝牙开始搜索等;
主要用到的几个方法:
getDefaultAdapter() 获取一个适配器对象
getName() 获取本地蓝牙名称
getAddress() 获取本地蓝牙的地址
enable() 打开本地蓝牙(低版本中不会提示用户)
isEnabled() 判断蓝牙是否已经打开
disable() 关闭本地蓝牙
startDiscovery() 开始搜索(发现)
cancelDiscovery() 取消设备搜索(发现)
isDiscovering() 返回当前是否是在搜索设备
listenUsingRfcommWithServiceRecord(String name, UUID uuid) 创建一个安全的BluetoothServerSocket
打开蓝牙
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
//打开蓝牙 方法一
if (!bluetoothAdapter.isEnabled()) {
bluetoothAdapter.enable();
}
//方法二 推荐
Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabler, REQUEST_ENABLE);
开始搜索设备
调用startDiscovery()
开始搜索设备,但是仅仅调用此方法是没有任何作用的,startDiscovery()
是异步调用,立即返回,需要注册一个广播接收者
来接受搜索的结果。整个搜索过程持续12秒。
//注册
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, intentFilter);
//广播接收者
BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//找到设备
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d("MainActivity", "搜索到设备");
//在列表中显示搜索出的设备
adapter.add("name : " + device.getName() + "\n address : " + device.getAddress());
bluetoothDevices.add(device);
}
//搜索完成
else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED
.equals(action)) {
Log.d("MainActivity", "搜索结束");
} else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
Log.d("MainActivity", "开始搜索");
}
}
};
连接设备
建立连接,Android sdk
支持的蓝牙连接是通过BluetoothSocket
建立连接,服务器端(BluetoothServerSocket
)和客户端(BluetoothSocket
)需指定同样的UUID,才能建立连接,因为建立连接的方法会阻塞线程,所以服务器端和客户端都应启动新线程连接。
UUID
的格式如下:
String uuid = "a60f35f0-b93a-11de-8a39-08002009c666";
建立服务器端代码如下:
首先建立服务器,获取BluetoothServerSocket
对象,通过BluetoothAdapter
的listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid)
方法和listenUsingRfcommWithServiceRecord(String name, UUID uuid)
方法,其中前者是不安全的链接,后者是安全的链接。
然后使用BluetoothServerSocket.accept()
方法接受客户端连接,当连接成功返回一个BluetoothSocket
对象。
private class ServiceThread extends Thread {
@Override
public void run() {
BluetoothServerSocket serverSocket = null;
try {
serverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord("my_test_lianjie", UUID.fromString(uuid));
Log.d("ServiceThread", "建立服务器成功");
} catch (IOException e) {
e.printStackTrace();
Log.d("ServiceThread", "建立服务器失败");
}
try {
socket = serverSocket.accept();
Log.d("ServiceThread", "客户端连接成功");
new ReadThread().start();
} catch (IOException e) {
e.printStackTrace();
Log.d("ServiceThread", "客户端连接失败");
}
}
}
建立客户端链接代码如下:
这里需要注意的是要对首先检测设备是否进行了配对,只有首先进行配对才能进行连接。蓝牙配对是通过反射的方法调用BluetoothDevice.creMethod()
方法。
建立连接首先获取BluetoothSocket
对象,调用BluetoothDevice
的createInsecureRfcommSocketToServiceRecord(UUID uuid)
或者createRfcommSocketToServiceRecord(UUID uuid)
方法,其中前者是不安全的链接,后者是安全的链接。
然后调用BluetoothSocket.connect()
方法进行连接。
private class ConnectThread extends Thread {
BluetoothDevice device;
public ConnectThread(BluetoothDevice device) {
this.device = device;
}
@Override
public void run() {
try {
socket = device.createRfcommSocketToServiceRecord(UUID.fromString(uuid));
} catch (IOException e) {
e.printStackTrace();
}
if (device.getBondState() == BluetoothDevice.BOND_BONDED) { //已配对设备 直接进行链接
connectSocket();
} else if (device.getBondState() == BluetoothDevice.BOND_NONE) { //未配对设备 先配对再链接
Method creMethod = null;
try {
creMethod = BluetoothDevice.class.getMethod("createBond");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
Log.e("TAG", "开始配对");
try {
creMethod.invoke(device);
} catch (IllegalAccessException e) {
e.printStackTrace();
Log.e("TAG", "配对失败");
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
private void connectSocket() {
try {
socket.connect();
Log.e("TAG", "连接成功");
new ReadThread().start();
} catch (IOException e) {
e.printStackTrace();
Log.e("TAG", "连接失败");
}
}
public void cancel() {
try {
socket.close();
socket = null;
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}
注意
客户端建立连接的方式有很多种,这里使用的是先获得一个socket
然后再检查是否需要进行配对,再建立连接。
还可以通过反射的方法,通过端口进行连接。
进行通讯
蓝牙通讯是通过流的方式进行的:
OutputStream outS = socket.getOutputStream();
InputStream inS = socket.getInputStream();
发送的方法如:
OutputStream os = null;
try {
os = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
try {
os.write("你好".getBytes());
Log.d("MainActivity", "发送成功");
} catch (IOException e) {
e.printStackTrace();
Log.d("MainActivity", "发送失败");
}
接受的方法如:
private class ReadThread extends Thread {
@Override
public void run() {
byte[] buffer = new byte[1024];
int bytes;
InputStream mmInStream = null;
try {
mmInStream = socket.getInputStream();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
while (true) {
try {
// Read from the InputStream
if ((bytes = mmInStream.read(buffer)) > 0) {
byte[] buf_data = new byte[bytes];
for (int i = 0; i < bytes; i++) {
buf_data[i] = buffer[i];
}
String s = new String(buf_data);
Log.d("ReadThread", s);
}
} catch (IOException e) {
try {
mmInStream.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
break;
}
}
}
}
至此,普通蓝牙的基本发现、连接、通讯功能就完成了,当然还有许多需要完善的地方。
写的可能有些混乱,不足之处,还请指正,不喜忽喷。