简单梳理android蓝牙配对通讯、app层实现蓝牙静默配对、ClsUtils类

最近做蓝牙通讯,需要app层静默连接通讯,记录近几天的心酸摸索。

1.搜索蓝牙设备

BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {//不支持蓝牙或者蓝牙不可用
            Toast.makeText(this, "设备不支持蓝牙", Toast.LENGTH_SHORT).show();
        } else {
        if (!bluetoothAdapter.isDiscovering()) bluetoothAdapter.startDiscovery();
                }

蓝牙发现新设备会发出广播:BluetoothDevice.ACTION_FOUND
蓝牙搜索结束会发出广播:BluetoothAdapter.ACTION_DISCOVERY_FINISHED
//自定义蓝牙搜索状态广播接收器,找到设备后更新自己的列表

   private class BlueDeviceReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(BluetoothDevice.ACTION_FOUND)) {//发现新设备
                BluetoothDevice btd = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if (!deviceList.contains(btd.getName() + "\n" + btd.getAddress())) {
                    deviceList.add(btd.getName() + "\n" + btd.getAddress());
                    deviceAdapter.notifyDataSetChanged();
                }
  } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
                  //搜索结束
                }
            }
        }
    }

注意添加权限:尖括号输入不了,囧。

uses-permission android:name=”android.permission.BLUETOOTH”
uses-permission android:name=”android.permission.BLUETOOTH_ADMIN”

如果是android6.0系统需要额外添加权限,否则无法搜索设备:

uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION”
uses-permission android:name=”android.permission.ACCESS_COARSE_LOCATION”

2.发起配对请求

    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     if (!bluetoothAdapter.checkBluetoothAddress(deviceAddress)) {//检查是否是有效的蓝牙地址
     Toast.makeText(LauncherDemoActivity.this, "蓝牙设备地址无效", Toast.LENGTH_SHORT).show();
            return false;
        }
    BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
    if (device != null) {
            Boolean returnValue = null;
            try {
                returnValue = ClsUtils.createBond(BluetoothDevice.class, device);
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (returnValue) {//发起配对成功,并不代表配对成功,因为可能被拒绝
                return true;
            }
        }

3.自定义蓝牙配对状态广播接收器,当有自己想要的设备配对时,就可以进行下一步socket通讯了

class MyActionReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
                int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
                        BluetoothDevice.ERROR);
                if (bondState == BluetoothDevice.BOND_BONDED) {//有新的配对设备
                        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
                        Set pairedDevices = adapter.getBondedDevices();
                        BluetoothDevice device1 = null;
// If there are paired devices
                        if (pairedDevices.size() > 0) {
                            // Loop through paired devices
                            for (BluetoothDevice device : pairedDevices) {
                      if(device.getAddress().equals("55:44:33:22:11:00")) {//如果连接的设备中有特定的设备则发起socket连接请求
                                    device1 = device;
                                break;
                                }
                            }
                        }
                        if(device1!=null) {//发起socket连接请求
                            try2ConnectDevice(device1);
                        }
                }
            }
    }
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
 BluetoothSocket mmSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
            this.device = device;
            try {
            // This is a blocking call and will only return on a
            // successful connection or an exception
            mmSocket.connect();
        } catch (IOException e) {
            connectionFailed();
            return;
        }    new CommunicateThread(mmSocket).start();       
        // 其中CommunicateThread,即和服务器交互线程如下:     
         //客户端交互线程
    class CommnicateThread extends Thread {
        private final BluetoothSocket mmSocket;
        private InputStream mmInStream;
        private OutputStream mmOutStream;
        private DataOutputStream dataOutputStream;
        private DataInputStream dataInputStream;
        public CommnicateThread(BluetoothSocket socket) {
            mmSocket = socket;
            // Get the BluetoothSocket input and output streams
            try {
                mmInStream = mmSocket.getInputStream();
                mmOutStream = mmSocket.getOutputStream();
                dataOutputStream = new DataOutputStream(mmOutStream);
                dataInputStream = new DataInputStream(mmInStream);
            } catch (IOException e) {
                Log.e(TAG, "temp sockets not created", e);
            }
        }
        public void run() {
            // Keep listening to the InputStream while connected
            while (true) {
                try {
                    int type = dataInputStream.readByte();
//                    System.out.println(type);
                } catch (IOException e) {
                    e.printStackTrace();
                    cancel();
                    connectionLost();
                    break;
                }
            }
        }
        //发送一个比特数组
        public void write(byte[] buffer) {
            try {
                dataOutputStream.write(buffer);
            } catch (IOException e) {
                Log.e(TAG, "Exception during write", e);
            }
        }
        //发送一个int整型数据
        public void writeInt(int i) {
            try {
                dataOutputStream.writeInt(i);
            } catch (IOException e) {
                Log.e(TAG, "Exception during write", e);
            }
        }
        //发送一个byte
        public void writeByte(byte b) {
            try {
                dataOutputStream.writeByte(b);
            } catch (IOException e) {
                Log.e(TAG, "Exception during write", e);
            }
        }
        public void cancel() {
            try {
                if (mmSocket != null)
                    mmSocket.close();
                if(dataInputStream!=null)dataInputStream.close();
                if(dataOutputStream!=null)dataOutputStream.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect socket failed", e);
            }
        }

4.服务器等待socket接入的代码,UUID需要和客户端一直,且是固定的貌似

BluetoothServerSocket bluetoothServerSocket;
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if(bluetoothAdapter==null){
            Log.d("BluetoothServerService","设备不支持蓝牙");
            return;
        }
        try {
            bluetoothServerSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord("ServerListener", MY_UUID);
        } catch (IOException e) {
            e.printStackTrace();
        } 
     /**
     * 阻塞式等待接入线程
     */

    class AcceptThread extends Thread {
        @Override
        public void run() {
            setName("acceptThread");
            BluetoothSocket socket = null;
            if (bluetoothServerSocket == null) return;
            setState(DISCONNECT);
            // Listen to the server socket if we're not connected
            while (mState != CONNECTED) {
                try {
                    // This is a blocking call and will only return on a
                    // successful connection or an exception
                    socket = bluetoothServerSocket.accept();
                } catch (IOException e) {
                    Log.e(TAG, "accept() failed", e);
                    break;
                }

                // If a connection was accepted
                if (socket != null) {
                    setState(CONNECTED);
                    commnicateThread = new CommnicateThread(socket);
                    commnicateThread.start();
                }
            }
        }
        public void cancel() {
            try {
                if (null != bluetoothServerSocket) {
                    bluetoothServerSocket.close();
                }
            } catch (IOException e) {
                Log.e(TAG, "close() of server failed", e);
            }
        }
    }
    //服务器端的交互线程
    class CommnicateThread extends Thread {
        private final BluetoothSocket mmSocket;
        private InputStream mmInStream;
        private OutputStream mmOutStream;
        private DataInputStream dataInputStream;
        private DataOutputStream dataOutputStream;
        public CommnicateThread(BluetoothSocket socket) {
            mmSocket = socket;

            // Get the BluetoothSocket input and output streams
            try {
                mmInStream = mmSocket.getInputStream();
                mmOutStream = mmSocket.getOutputStream();
                dataInputStream = new DataInputStream(mmInStream);
                dataOutputStream = new DataOutputStream(mmOutStream);
            } catch (IOException e) {
                Log.e(TAG, "temp sockets not created", e);
            }

        }

        public void run() {

            // Keep listening to the InputStream while connected
            while (true) {
                try {
                    int type = dataInputStream.readByte();
                    if (type == 1) {
                        short length = dataInputStream.readShort();
                        int what = dataInputStream.readByte();
                        System.out.println("1号类型:指令");
                        sendOrderBroadcast(what);
                    } else if (type == 2) {
                        System.out.println("2号类型:javabean");
                        byte[] data;
                        int flag;
                        synchronized (this) {
                            flag = dataInputStream.readInt();
                            int length = dataInputStream.readInt();
                            data = new byte[length];
                            dataInputStream.readFully(data);
                        }
                        System.out.println(data.length);
                        InstantNotificationItem i = (InstantNotificationItem) ByteUtil.toObject(data);
                        if (i == null) System.out.println("null");
                        else {
                            sendNotificationChangeBroadcast(i, flag);
                        }
                    }
                    writeAbyte((byte) 1);//回执给客户端的字符,暂时没有使用

                } catch (IOException e) {
                    Log.e(TAG, "连接中断", e);
                    cancel();
                    connectionLost();
                    break;
                }
            }
        }

        /**
         * @param buffer The bytes to write
         */
        public void write(byte[] buffer) {
            try {
                mmOutStream.write(buffer);
            } catch (IOException e) {
                Log.e(TAG, "Exception during write", e);
            }
        }
        public void writeAbyte(byte what){

            try {
                dataOutputStream.writeByte(what);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        public void cancel() {
            try {
                if (mmSocket != null)
                    mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect socket failed", e);
            }
        }
    }

**

附:自定义蓝牙配对弹框、自动连接

**
公司要求在app层自定义蓝牙配对弹框,自动连接,网上搜了半天,没有很好的代码,今天把自己写的代码分享出来。话不多说,原理很简单,就是那个配对和取消的弹框是通过广播弹出来的,我们需要做的就是,自定义广播将其拦截,做自己的逻辑判断就可以了。下边是自动匹配所有蓝牙请求的代码,记得设置最大优先级哦,将IntentFilter的优先级或者静态注册的话android:priority=”2147483647”。自定义透明activity当做弹出框,点击某一个按钮的时候再device.setPairingConfirmation(true),简单吧。

public class BluetoothConnectActivityReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) {
            BluetoothDevice device = intent
                    .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            try {
                // 广播接收者的优先者为最高 收到广播后 停止广播向下传递
                // TODO: 16/8/9 判断device是否是自己的设备,是的话拦截,否则不处理。
              device.setPairingConfirmation(true);
                abortBroadcast();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    }

**

附录2:使用反射调用私有方法的ClsUtils.java文件

**

import android.bluetooth.BluetoothDevice;
import android.util.Log;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ClsUtils {


    /**
     * 与设备配对 参考源码:platform/packages/apps/Settings.git
     * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
     */
    static public boolean createBond(Class btClass, BluetoothDevice btDevice)
            throws Exception {

        Method createBondMethod = btClass.getMethod("createBond");
        Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
        return returnValue.booleanValue();

    }

    /**
     * 与设备解除配对 参考源码:platform/packages/apps/Settings.git
     * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
     */
    static public boolean removeBond(Class btClass, BluetoothDevice btDevice)
            throws Exception {

        Method removeBondMethod = btClass.getMethod("removeBond");
        Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
        return returnValue.booleanValue();

    }

    static public boolean setPin(Class btClass, BluetoothDevice btDevice,
                                 String str) throws Exception {

        try {

            Method removeBondMethod = btClass.getDeclaredMethod("setPin",
                    new Class[]
                            {
                                    byte[].class
                            });
            Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice,
                    new Object[]
                            {
                                    str.getBytes()
                            });
            Log.e("returnValue", "" + returnValue);

        } catch (SecurityException e) {

// throw new RuntimeException(e.getMessage());
            e.printStackTrace();

        } catch (IllegalArgumentException e) {

// throw new RuntimeException(e.getMessage());
            e.printStackTrace();

        } catch (Exception e) {

// TODO Auto-generated catch block
            e.printStackTrace();

        }
        return true;


    }

    static public boolean setPassKey(Class btClass, BluetoothDevice btDevice,
                                     String str) throws Exception {

        try {

            Method removeBondMethod = btClass.getDeclaredMethod("setPasskey",
                    new Class[]
                            {
                                    byte[].class
                            });
            Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice,
                    new Object[]
                            {
                                    str.getBytes()
                            });
            Log.e("returnValue", "" + returnValue);

        } catch (SecurityException e) {

// throw new RuntimeException(e.getMessage());
            e.printStackTrace();

        } catch (IllegalArgumentException e) {

// throw new RuntimeException(e.getMessage());
            e.printStackTrace();

        } catch (Exception e) {

// TODO Auto-generated catch block
            e.printStackTrace();

        }
        return true;


    }

    // 取消用户输入
    static public boolean cancelPairingUserInput(Class btClass,
                                                 BluetoothDevice device)

            throws Exception {

        Method createBondMethod = btClass.getMethod("cancelPairingUserInput");
// cancelBondProcess()
        Boolean returnValue = (Boolean) createBondMethod.invoke(device);
        return returnValue.booleanValue();

    }

    // 取消配对
    static public boolean cancelBondProcess(Class btClass,
                                            BluetoothDevice device)

            throws Exception {

        Method createBondMethod = btClass.getMethod("cancelBondProcess");
        Boolean returnValue = (Boolean) createBondMethod.invoke(device);
        return returnValue.booleanValue();

    }

    /**
     *
     * @param clsShow
     */
    static public void printAllInform(Class clsShow) {

        try {

// 取得所有方法
            Method[] hideMethod = clsShow.getMethods();
            int i = 0;
            for (; i < hideMethod.length; i++) {

                Log.e("method name", hideMethod[i].getName() + ";and the i is:"
                        + i);

            }
// 取得所有常量
            Field[] allFields = clsShow.getFields();
            for (i = 0; i < allFields.length; i++) {

                Log.e("Field name", allFields[i].getName());

            }

        } catch (SecurityException e) {

// throw new RuntimeException(e.getMessage());
            e.printStackTrace();

        } catch (IllegalArgumentException e) {

// throw new RuntimeException(e.getMessage());
            e.printStackTrace();

        } catch (Exception e) {

// TODO Auto-generated catch block
            e.printStackTrace();

        }

    }

    public static boolean isWantedMac(String macStr) {
        if (macStr == null || macStr.isEmpty()) return false;
        if (macStr.equals("55:44:33:22:11:00")) {
            return true;
        } else return false;
    }

}

你可能感兴趣的:(android笔记)