Android 蓝牙

MVP 结构的Android 蓝牙 Demo

MVP

整个代码采用MVP 结构来写。和其他MVP模式大同小异,
Android 蓝牙_第1张图片

由于Bluetooth 发现配对连接过程比较长,整个UI的布局功能不是特别的清晰。只能作为参考了。
比较难处理的是Adapter, 在这里把BtAdapter 拆分为三个部分,BtViewHolder 负责在Adapter 的getView 中处理View相关。Adapter中的数据放在了Module 中,BtAdapter本身放在了Present 层。

自动配对原理

Android 蓝牙连接的过程分为发现,配对,连接。 在配对中主要有三个过程1. creatBound, setPairingConfirmation, setPin. 研究Android Setting 的代码,蓝牙连接的过程如下:
Android 蓝牙_第2张图片
根据上图可以看出,如果要实现自动配对请求需要拦截 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

你可能感兴趣的:(android)