MVP 结构的Android 蓝牙 Demo
MVP
整个代码采用MVP 结构来写。和其他MVP模式大同小异,
由于Bluetooth 发现配对连接过程比较长,整个UI的布局功能不是特别的清晰。只能作为参考了。
比较难处理的是Adapter, 在这里把BtAdapter 拆分为三个部分,BtViewHolder 负责在Adapter 的getView 中处理View相关。Adapter中的数据放在了Module 中,BtAdapter本身放在了Present 层。
自动配对原理
Android 蓝牙连接的过程分为发现,配对,连接。 在配对中主要有三个过程1. creatBound, setPairingConfirmation, setPin. 研究Android Setting 的代码,蓝牙连接的过程如下:
根据上图可以看出,如果要实现自动配对请求需要拦截 BluetoothDevice.ACTION_PAIRING_REQUEST 广播,避免BluetoothPairingDialog这个Dialog 启动,BluetoothDevice.ACTION_PAIRING_REQUEST 这个广播为有序广播,可以定义优先级高的Receiver,接受后abortBroadcast。
Android 对蓝牙API的控制比较严格,只暴露了部分API,通过反射的方法调用部分APK。
public class BluetoothTools {
static public boolean pair(BluetoothDevice device){
return createBond(device);
}
static public boolean unpair(BluetoothDevice device) {
int state = device.getBondState();
if (state == BluetoothDevice.BOND_BONDING) {
cancelBondProcess(device); }
if (state != BluetoothDevice.BOND_NONE) {
final boolean successful = removeBond(device);
return successful;
}
return false;
}
static public void setPin(BluetoothDevice device, String pin){
try {
device.setPin(pin.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
static public void setPairingConfirmation(BluetoothDevice device){
// device.setPairingConfirmation(true);
try {
Field field = device.getClass().getDeclaredField("sService");
field.setAccessible(true);
Object service = field.get(device);
Method method = service.getClass().getDeclaredMethod("setPairingConfirmation",
BluetoothDevice.class, boolean.class);
method.setAccessible(true);
method.invoke(service, device, true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
static private boolean createBond(BluetoothDevice device){
return device.createBond();
}
static public boolean removeBond(BluetoothDevice device){
boolean result = false;
try {
Method method = null;
method = device.getClass().getDeclaredMethod("removeBond");
method.setAccessible(true);
result = (boolean) method.invoke(device);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return result;
}
static private void cancelBondProcess(BluetoothDevice device){
try {
Method method = null;
method = device.getClass().getDeclaredMethod("cancelBondProcess");
method.setAccessible(true);
method.invoke(device);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
在Android7.0的版本上,对setPairingConfirmation函数加了权限控制,非系统APK无法调用。采用两次反射的方法调用,还是无法绕过。所以在7.0版本上的自动配对还需要研究。
04-20 13:50:50.588 7517-7517/com.example.bluetoothapplication W/System.err: Caused by: java.lang.SecurityException: Need BLUETOOTH PRIVILEGED permission: Neither user 10046 nor current process has android.permission.BLUETOOTH_PRIVILEGED.
04-20 13:50:50.588 7517-7517/com.example.bluetoothapplication W/System.err: at android.os.Parcel.readException(Parcel.java:1602)
04-20 13:50:50.589 7517-7517/com.example.bluetoothapplication W/System.err: at android.os.Parcel.readException(Parcel.java:1555)
04-20 13:50:50.589 7517-7517/com.example.bluetoothapplication W/System.err: at android.bluetooth.IBluetooth$Stub$Proxy.setPairingConfirmation(IBluetooth.java:1593)
04-20 13:50:50.589 7517-7517/com.example.bluetoothapplication W/System.err: ... 11 more
static public void setPairingConfirmation(BluetoothDevice device){
// device.setPairingConfirmation(true);
try {
Field field = device.getClass().getDeclaredField("sService");
field.setAccessible(true);
Object service = field.get(device);
Method method = service.getClass().getDeclaredMethod("setPairingConfirmation",
BluetoothDevice.class, boolean.class);
method.setAccessible(true);
method.invoke(service, device, true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
这里有一个参考,不过好像没有在7.0的版本上测试。
Android蓝牙自动配对Demo,亲测好使!!!
Code:
github