Android 蓝牙编程
Android 蓝牙开发分两种:
经典蓝牙开发
蓝牙3.0及以下版本的蓝牙,都称为“经典蓝牙”。特点是功耗高、传输数据量大、传输距离只有10米。主要场景有蓝牙耳机、蓝牙音箱等。是近距离音频传输的不二选择
低功耗蓝(BLE)牙开发
蓝牙4.0及以上版本,低功耗,传输数据量小,距离50米左右。主要场景有共享单车锁、蓝牙防丢器等,是目前手机和智能硬件通信性价比最高的手段
一般较新的手机都是双模蓝牙,即同时支持经典蓝牙和低功耗蓝牙。
关蓝牙开发的官方文档教程,请查看 这里
以经典蓝牙开发为例
- 开启蓝牙
- 扫描蓝牙
- 配对蓝牙
- 连接蓝牙
- 通信
权限配置:
<manifest ... >
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
...
</manifest>
对于一些BLE的低功耗蓝牙设备,需要用到上述的一个位置相关的权限,否则可能存在扫描不到这种设备的情况。但是需要注意,该权限在6.0以上设备,属于动态权限,请处理动态权限申请相关的事宜。
操作代码:
public class BluetoothHelper {
public BluetoothHelper(){
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
private BluetoothAdapter mBluetoothAdapter;
// 设备是否支持蓝牙
public boolean isSupport() {
return mBluetoothAdapter != null;
}
// 蓝牙是否已经开启
public boolean isOpen() {
return isSupport() && mBluetoothAdapter.isEnabled();
}
// 向用户请求开启蓝牙。同步执行
public void requestBluetooth(int requestCode, Activity context) {
if (isSupport()){
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
context.startActivityForResult(enableIntent, requestCode);
}
}
// 静默开启蓝牙(不会弹出提示)。异步执行
public void openBluetooth(){
if(isSupport()){
mBluetoothAdapter.enable();
}
}
// 关闭蓝牙适配器
public void closeBluetooth(){
if (isSupport()){
mBluetoothAdapter.disable();
}
}
// 查询已配对设备
public List<BluetoothDevice> checkDevices() {
List<BluetoothDevice> devices= new ArrayList<>();
if(isSupport()){
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices != null && pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
devices.add(device);
}
}
}
return devices;
}
// 扫描新设备
public void scanNewDevice() {
// 该方法立即返回一个布尔值,表示扫描是否已成功启动。
// 内部会启动了一个子线程执行扫描,扫描通常需要大约12秒
if(isOpen() && !mBluetoothAdapter.isDiscovering()){
if (mBluetoothAdapter.startDiscovery()) {
Log.d("","-- 已成功启动寻找新设备 --");
} else {
Log.d("","-- 启动寻找新设备失败 --");
}
}
}
// 取消扫描。返回 true 表示成功取消
public boolean cancelScan(){
return isSupport()
&& mBluetoothAdapter.isDiscovering()
&& mBluetoothAdapter.cancelDiscovery();
}
public void register(Context ctx){
IntentFilter intentFilter = new IntentFilter();
// 开始扫描
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
// 扫描结束(重新扫描时,会先终止先前的扫描)
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
// 扫描到设备(每扫描一个发送一条广播)
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
// 本地蓝牙适配器的状态更改(蓝牙打开或关闭)
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
// 蓝牙适配器的连接状态改变(另一个配对设备自己把连接断开)
intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
// intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
ctx.registerReceiver(mBluetoothReceiver,intentFilter);
}
public void unregister(Context ctx){
ctx.unregisterReceiver(mBluetoothReceiver);
}
private BroadcastReceiver mBluetoothReceiver = 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);
Log.d("BluetoothHelper","设备名:"+device.getName()+" *=* " +"MAC地址:"+device.getAddress());
}else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)){
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
switch (state) {
case BluetoothAdapter.STATE_TURNING_ON:
Log.d("BluetoothHelper","蓝牙正在打开.");
break;
case BluetoothAdapter.STATE_ON:
Log.d("BluetoothHelper","蓝牙已打开.");
break;
case BluetoothAdapter.STATE_TURNING_OFF:
Log.d("BluetoothHelper","蓝牙正在关闭.");
break;
case BluetoothAdapter.STATE_OFF:
Log.d("BluetoothHelper","蓝牙已关闭.");
break;
}
}else if (BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)){
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, -1);
switch (state) {
case BluetoothAdapter.STATE_CONNECTING:
case BluetoothAdapter.STATE_CONNECTED:
case BluetoothAdapter.STATE_DISCONNECTING:
case BluetoothAdapter.STATE_DISCONNECTED:
break;
}
}
}
};
}
需要注意,以向用户请求的方式开启蓝牙,应当在Activity中处理返回结果:
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_ENABLE_BLUETOOTH) {
if (resultCode == RESULT_OK) {
// 蓝牙打开成功
} else {
// 蓝牙打开失败
}
}
}
判断设备是否处于连接中
注意,BluetoothAdapter.getBondedDevices()获取的是已配对设备列表,要想确定设备是否处于连接中,通过查询源码,可知BluetoothDevice类中有一个isConnected方法,但该方法是隐藏的,非对外公开的API,因此只能通过反射调用
for (BluetoothDevice device : pairedDevices) {
Method isConnectedMethod = null;
try {
isConnectedMethod = BluetoothDevice.class.getDeclaredMethod("isConnected");
boolean isConnected = (boolean) isConnectedMethod.invoke(device);
Log.d("BluetoothHelper","Devices:"+device.getName()+" = "+isConnected);
} catch (Exception e) {
Log.e("BluetoothHelper",e.getMessage());
e.printStackTrace();
}
}
如果只是判断蓝牙耳机是否连接,可以增加以下方法:
public boolean isBluetoothHeadsetConnected() {
int a2dp = mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.A2DP);
int headset = mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET);
return isOpen() && (
headset == BluetoothHeadset.STATE_CONNECTED
|| a2dp == BluetoothA2dp.STATE_CONNECTED);
}
拓展
如果需要通过代码来控制配对(新版本已不允许使用代码操作进行配对)
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public void pin(BluetoothDevice device){
//配对之前关闭扫描
if (mBluetoothAdapter.isDiscovering()){
mBluetoothAdapter.cancelDiscovery();
}
//判断设备是否配对,已配对则不需配对了
if (device.getBondState() == BluetoothDevice.BOND_NONE) {
device.createBond();
}
}
createBond是一个异步调用的方法,它不会返回配到的结果,如需获取蓝牙是否配对成功的结果,需要监听广播
// 来自其他设备的配对请求广播
intentFilter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
// 当前配对状态广播
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
处理广播:
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//1.确认配对
device.setPairingConfirmation(true);
//2.终止有序广播
abortBroadcast();
//3.调用setPin方法进行配对
device.setPin("0000".getBytes());
}else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)){
int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
switch (state) {
case BluetoothDevice.BOND_NONE:
// 取消配对
break;
case BluetoothDevice.BOND_BONDING:
// 配对中
break;
case BluetoothDevice.BOND_BONDED:
// 配对成功
break;
}
}
}
注意:执行上述操作,需要申请<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />权限,但是新版本上,已经不允许第三方APP申请该权限
公众号“编程之路从0到1”