Android BLE API
BluetoothAdapter
拥有基本的蓝牙操作,可以把它当作蓝牙管理器。
- getDefaultAdapter:静态方法,获取默认的蓝牙适配器对象;
- enable:打开蓝牙功能;
- disable:关闭蓝牙功能;
- isEnable:判断蓝牙功能是否打开;
- startDiscovery:开始搜索周围的蓝牙设备;
- cancelDiscovery:取消搜索操作;
- isDiscovering:判断当前是否正在搜索设备;
- getBondedDevices:获取已绑定的设备列表;
- setName:设置本机的蓝牙名称;
- getName:获取本机的蓝牙名称;
- getAddress:获取本机的蓝牙地址;
- getRemoteDevice:根据蓝牙地址获取远程的蓝牙设备;
- getState:获取本地蓝牙适配器的状态;
- listenUsingRfcommWithServiceRecord:根据名称和UUID创建并返回BluetoothServiceSocket;
- listenUsingRfcommOn:根据渠道编号创建并返回BluetoothServiceSocket。
BluetoothDevice
用于指代某个蓝牙设备,通常表示对方设备。BluetoothAdapter管理的是本机蓝牙设备。
- getName:获得该设备的名称;
- getAddress:获得该设备的地址;
- getBondState:获得该设备的绑定状态;
- createBond:创建匹配对象;
- createRfcommSocketToServiceRecord:根据UUID创建并返回一个BluetoothSocket。
BluetoothGatt
这个类提供了 Bluetooth GATT 的基本功能。例如重新连接蓝牙设备,发现蓝牙设备的 Service 等等。
BluetoothGattService
这一个类通过 BluetoothGatt#getService 获得,如果当前服务不可见那么将返回一个 null。我们可以通过这个类的 getCharacteristic(UUID uuid) 进一步获取 Characteristic 实现蓝牙数据的双向传输。
BluetoothGattCharacteristic
通过这个类定义需要往外围设备写入的数据和读取外围设备发送过来的数据。
DEMO
1. 声明所需要的权限
<!--使用蓝牙所需要的权限-->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<!--使用扫描和设置蓝牙的权限(要使用这一个权限必须申明BLUETOOTH权限)-->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!--
在 Android 6.0 及以上,还需要打开位置权限。
如果应用没有位置权限,蓝牙扫描功能不能使用(其它蓝牙操作例如连接蓝牙设备和写入数据不受影响)。
-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
2. 在 Android 6.0 及以上,获取基于地理位置的动态权限
/**
* 获取基于地理位置的动态权限
*/
private void bluetoothPermissions() {
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{
android.Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
}
}
3. 获取 BluetoothAdapter
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
4. 开启蓝牙
/**
* 检测到蓝牙是否开启
* 若没开启,弹出对话框让用户开启蓝牙
*/
private void openBluetooth(){
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
5. 扫描蓝牙设备
外围设备开启蓝牙后,会广播出许多的关于该设备的数据信息,例如 mac 地址,uuid 等等。通过这些数据我们可以筛选出需要的设备。
在 BluetoothAdapter 中,我们可以看到有两个扫描蓝牙的方法。
第一个方法可以指定只扫描含有特定 UUID Service 的蓝牙设备,
boolean startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback)
第二个方法则是扫描全部蓝牙设备。
boolean startLeScan(BluetoothAdapter.LeScanCallback callback)
由于蓝牙扫描的操作比较消耗手机的能量。所以我们不能一直开着蓝牙,必须设置一段时间之后关闭蓝牙扫描。
/**
* 扫描蓝牙设备
*
* @param enable
*/
private void scanLeDevice(final boolean enable) {
final BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
/**
* 一旦发现蓝牙设备,LeScanCallback 就会被回调,直到 stopLeScan 被调用。
* 出现在回调中的设备会重复出现,所以如果我们需要通过 BluetoothDevice 获取外围设备的地址手动过滤掉已经发现的外围设备。
*
* @param device 蓝牙设备的类,可以通过这个类建立蓝牙连接获取关于这一个设备的一系列详细的参数,例如名字,MAC 地址等等;
* @param rssi 蓝牙的信号强弱指标,通过蓝牙的信号指标,我们可以大概计算出蓝牙设备离手机的距离。计算公式为:d = 10^((abs(RSSI) - A) / (10 * n))
* @param scanRecord 蓝牙广播出来的广告数据。
*/
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
//重复过滤方法,列表中包含不该设备才加入列表中,并刷新列表
if (!deviceList.contains(device)) {
//将设备加入列表数据中
deviceList.add(device);
}
}
};
if (enable) {
deviceList.clear();
// 预先定义停止蓝牙扫描的时间(因为蓝牙扫描需要消耗较多的电量)
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
showDevices();
}
}, SCAN_PERIOD);
// 定义一个回调接口供扫描结束处理
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
6. 连接蓝牙设备
连接蓝牙设备可以通过 BluetoothDevice#ConnectGatt 方法连接,也可以通过 BluetoothGatt#connect 方法进行重新连接。
当调用蓝牙的连接方法之后,蓝牙会异步执行蓝牙连接的操作,如果连接成功会回调 BluetoothGattCalbackl#onConnectionStateChange 方法。这个方法运行的线程是一个 Binder 线程,所以不建议直接在这个线程处理耗时的任务,因为这可能导致蓝牙相关的线程被阻塞。
private BluetoothGatt mBluetoothGatt;
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
/**
* 连接状态改变
*
* @param gatt 蓝牙设备的 Gatt 服务连接类
* @param status 代表是否成功执行了连接操作,
* 如果为 BluetoothGatt.GATT_SUCCESS 表示成功执行连接操作,
* 第三个参数才有效,否则说明这次连接尝试不成功
* @param newState 代表当前设备的连接状态,
* 如果 newState == BluetoothProfile.STATE_CONNECTED 说明设备已经连接,
* 可以进行下一步的操作了(发现蓝牙服务,也就是 Service)。
* 当蓝牙设备断开连接时,这一个方法也会被回调,
* 其中的 newState == BluetoothProfile.STATE_DISCONNECTED
*/
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
Log.e(TAG, "连接状态:" + newState);
if (BluetoothGatt.STATE_CONNECTED == newState) {
Log.e(TAG, "连接成功:");
//必须有,可以让onServicesDiscovered显示所有Services
gatt.discoverServices();
Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_LONG).show();
} else if (BluetoothGatt.STATE_DISCONNECTED == newState) {
Log.e(TAG, "断开连接:");
Toast.makeText(MainActivity.this, "断开连接", Toast.LENGTH_SHORT).show();
}
}
};
/**
* 连接蓝牙
*
* @param index
*/
private void connectBluetoothDevice(int index) {
BluetoothDevice bluetoothDevice = deviceList.get(index);
Toast.makeText(MainActivity.this, bluetoothDevice.getAddress(), Toast.LENGTH_LONG).show();
/**
* autoConnect 是否需要自动连接。true, 表示如果设备断开了,会不断的尝试自动连接。false, 表示只进行一次连接尝试。
* mGattCallback 连接后进行的一系列操作的回调类。
*/
mBluetoothGatt = bluetoothDevice.connectGatt(this, true, mGattCallback);
}
7. 发现服务
在成功连接到蓝牙设备之后才能进行这一个步骤,也就是说在 BluetoothGattCalbackl#onConnectionStateChang 方法被成功回调且表示成功连接之后调用 BluetoothGatt#discoverService 这一个方法。当这一个方法被调用之后,系统会异步执行发现服务的过程,直到 BluetoothGattCallback#onServicesDiscovered 被系统回调之后,手机设备和蓝牙设备才算是真正建立了可通信的连接。
/**
* 发现服务,在蓝牙连接的时候会调用
* @param gatt
* @param status
*/
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
}
List<BluetoothGattService> list = mBluetoothGatt.getServices();
for (BluetoothGattService bluetoothGattService : list) {
String str = bluetoothGattService.getUuid().toString();
Log.e(TAG, " BluetoothGattService:" + str);
List<BluetoothGattCharacteristic> gattCharacteristics = bluetoothGattService
.getCharacteristics();
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
Log.e(TAG, " BluetoothGattCharacteristic:" + gattCharacteristic.getUuid());
if ("00002a19-0000-1000-8000-00805f9b34fb".equals(gattCharacteristic.getUuid().toString())) {
BluetoothGattCharacteristic alertLevel = gattCharacteristic;
Log.e(TAG, alertLevel.getUuid().toString());
gatt.readCharacteristic(alertLevel);
}
}
}
}
8. 读取数据
当我们发现服务之后就可以通过 BluetoothGatt#getService 获取 BluetoothGattService,接着通过 BluetoothGattService#getCharactristic 获取 BluetoothGattCharactristic。
通过 BluetoothGattCharactristic#readCharacteristic 方法可以通知系统去读取特定的数据。如果系统读取到了蓝牙设备发送过来的数据就会调用 BluetoothGattCallback#onCharacteristicRead 方法。通过 BluetoothGattCharacteristic#getValue 可以读取到蓝牙设备的数据。
@Override
public void onCharacteristicRead(final BluetoothGatt gatt,
final BluetoothGattCharacteristic characteristic,
final int status) {
Log.e(TAG, "callback characteristic read status " + status
+ " characteristic " + characteristic);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "read value: " + characteristic.getValue());
}
}
};
代码
https://github.com/DavidJi80/Android
BlueTooth
v0.7
参考
https://www.jianshu.com/p/3a372af38103
https://blog.csdn.net/fukaimei/article/details/78837578
https://blog.csdn.net/uu13567/article/details/78017347
https://www.jb51.net/article/124942.htm