android 蓝牙 ble

    如果不了解androd ble就先学ble整个得大致通信流程,如果大致通信流程了解了,就利用封装好得 ble库,应该对你帮助很大。


 android ble连接数据大致也没几个步骤,但是对于刚涉水蓝牙的小伙伴可能会一脸蒙蔽,怎么弄就是不成功,下边讲解,直接从代码中讲解


1.封装号的核心ble通讯层,这个可以解决,完整流程通讯和长时间通讯

package com.reformer.bles;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.os.Handler;
import android.os.Message;

import com.reformer.utils.commen.LogUtil;

import java.util.UUID;

public class Ble extends BluetoothGattCallback {
    private UUID UUID_SERVICE = UUID.fromString("0000fde6-0000-1000-8000-00805f9b34fb");
    private UUID UUID_SERVICE2 = UUID.fromString("0000fdeb-0000-1000-8000-00805f9b34fb");//梯控
    private UUID UUID_WRITE = UUID.fromString("0000fde7-0000-1000-8000-00805f9b34fb");
    private UUID UUID_INDICATE = UUID.fromString("0000fde8-0000-1000-8000-00805f9b34fb");
    private UUID UUID_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");

    private Context mCtx;
    private BluetoothAdapter mBtAdapter;
    private String mAddress;
    private OnBleListener mListner;
    private BluetoothGattCharacteristic mWriteChar;
    private BluetoothGatt mBluetoothGatt;
    private byte[] tempCmd2 = null;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    removeMessages(1);
                    break;
            }
        }
    };

    public Ble(Context ctx) {
        final BluetoothManager bluetoothManager = (BluetoothManager)
                ctx.getSystemService(Context.BLUETOOTH_SERVICE);
        mBtAdapter = bluetoothManager.getAdapter();
        mCtx = ctx;
    }

    public boolean scanStart() {
        LogUtil.d("扫描");
        return mBtAdapter != null && mBtAdapter.startLeScan(null, m18LeScanCallback);
    }

    public void scanStop() {
        if (mBtAdapter != null)//华为:G7-Ul20;CHM-00;G7-TL00;蓝牙4.0,会出现空指针异常
            mBtAdapter.stopLeScan(m18LeScanCallback);
    }

    public boolean writeChar(byte[] bytes) {
        LogUtil.d("数据写入___" + Utils.bytes2String(bytes));
        if (mWriteChar == null || mBluetoothGatt == null || bytes == null)
            return false;
        if (bytes.length <= 20) {
            return writeCharInner(bytes);
        } else {
            byte[] tempCmd1 = new byte[20];
            for (int i = 0; i < 20; i++)
                tempCmd1[i] = bytes[i];

            tempCmd2 = new byte[bytes.length - 20];
            for (int i = 0; i < bytes.length - 20; i++)
                tempCmd2[i] = bytes[i + 20];
            return writeCharInner(tempCmd1);
        }
    }

    private boolean writeCharInner(byte[] bytes) {//写特征值
        LogUtil.d("数据写入_分包__" + Utils.bytes2String(bytes));
        return mWriteChar.setValue(bytes) && mBluetoothGatt.writeCharacteristic(mWriteChar);
    }

    public int connect(String address) {
        LogUtil.d("连接");
        isOk = true;
        return connectGatt(address);
    }

    public void close() {
        LogUtil.d("关闭");
        isOk = false;
        closeGatt();
    }

    private void closeGatt() {
        if (mBluetoothGatt != null) {
            mBluetoothGatt.disconnect();
            if (mBluetoothGatt != null) {
                mBluetoothGatt.close();
                if (mBluetoothGatt != null)
                    mBluetoothGatt = null;
            }
        }
        if (mBtAdapter != null) {
            if (mAddress != null && !mAddress.equals("")) {
                BluetoothDevice device = mBtAdapter.getRemoteDevice(mAddress);
                if (device != null) {
                    if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
                        try {
                            Utils.removeBond(BluetoothDevice.class, device);//适配魅族某款手机
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    private int connectGatt(String address) {
        if (!isOk)
            return 400;
        mAddress = address;
        if (mListner != null)
            mListner.onConnectStatus(1);
        closeGatt();
        if (mBtAdapter == null) {
            return 201;
        }
        BluetoothDevice device = mBtAdapter.getRemoteDevice(mAddress);
        if (device == null) {
            return 202;
        }
        mBluetoothGatt = device.connectGatt(mCtx, false, this);//第一步:连接gatt:获取gatt管道
        return 400;
    }

    private boolean isOk = false;

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status,
                                        int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            LogUtil.d("连接状态成功");
            gatt.discoverServices();//第二步:gatt连接成功,发现服务
        } else {
            LogUtil.d("连接状态失败");
            connect(mAddress);
        }
    }

    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == 0) {//第三步:发现服务成功;获取服务,订阅特征值
            LogUtil.d("连接服务成功");
            BluetoothGattService service = gatt.getService(UUID_SERVICE);
            mWriteChar = service.getCharacteristic(UUID_WRITE);
            BluetoothGattCharacteristic mIndicateChar = service.getCharacteristic(UUID_INDICATE);
            gatt.setCharacteristicNotification(mIndicateChar, true);//写入特征值改变时,通知
            BluetoothGattDescriptor descriptor = mIndicateChar.getDescriptor(UUID_DESCRIPTOR);//获取修饰
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);//修饰配置
            gatt.writeDescriptor(descriptor);
        } else {
            LogUtil.d("连接服务失败");
            connect(mAddress);
        }
    }

    @Override
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        super.onDescriptorWrite(gatt, descriptor, status);
        if (status == 0 && mListner != null) {//第四步:订阅特征值成功;发送数据;交换随即密钥
            LogUtil.d("订阅成功");
            mListner.onReady();
            mListner.onConnectStatus(0);
        } else {
            connect(mAddress);
        }
    }

    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        if (status == 0 && mListner != null) {//第六步:写入第一包数据后睡眠,发送第二包数据
            if (tempCmd2 != null) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                byte[] a = tempCmd2;
                tempCmd2 = null;
                writeCharInner(a);
                LogUtil.d("第二包数据写入" + Utils.bytes2String(a));
            }
        } else {
            connect(mAddress);
        }
    }

    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        LogUtil.d("数据接受成功");
        if (mListner != null)
            mListner.onReceive(characteristic.getValue());
    }

    public void setOnBleListener(OnBleListener bleListener) {
        mListner = bleListener;
    }

    public abstract static class OnBleListener {

        public void onConnectStatus(int status) {

        }

        public void onReady() {

        }

        public abstract void onReceive(byte[] value);

        public abstract void onScanResult(String name, String address, int rssi, byte[] mac);
    }

    private BluetoothAdapter.LeScanCallback m18LeScanCallback = new BluetoothAdapter.LeScanCallback() {

        @Override
        public void onLeScan(BluetoothDevice device, int rssi, byte[] bytes) {
            LogUtil.d("device__" + device.getAddress().toString());
            if (mListner == null)
                return;
            byte[] macTemp = new byte[9];
            System.arraycopy(bytes, 18, macTemp, 0, 9);
            mListner.onScanResult(device.getName(), device.getAddress(), rssi, macTemp);
        }
    };
}
//    private ScanCallback m21ScanCallback = new ScanCallback() {
//        @Override
//        public void onScanResult(int callbackType, ScanResult result) {
//            byte[] scanRecord = result.getScanRecord().getBytes();
//            BluetoothDevice device = result.getDevice();
//            int rssi = result.getRssi();
////            foundNewDevice(scanRecord, device, rssi);
//        }
//    };

2.通过ble传送数据,这个类,接受数据处理数据

 
 
package com.reformer.bles;

import android.content.Context;
import android.os.Handler;
import android.os.Message;

import com.reformer.bles.encrypt.KeyBase;
import com.reformer.bles.encrypt.KeyKeyfree;
import com.reformer.utils.commen.LogUtil;

import java.util.ArrayList;

public class Presenter {
    private byte[] mMac;
    private OnStateListener mOnStateListener;
    private OnScanListener mOnScanListener;
    private ArrayList<BleBean> mScans = new ArrayList<BleBean>();
    private Ble mBle;
    public int mTimeOut = 6000;//超时时间
    private KeyBase mKey;

    private String bytes2HexString(byte[] src) {//字节数组转16进制字符串
        StringBuffer sb = new StringBuffer();
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                sb.append(0);
            }
            sb.append(hv);
        }
        return sb.toString();
    }

    public Presenter(Context ctx) {
        mKey = new KeyKeyfree();
        mBle = new Ble(ctx);
        mBle.setOnBleListener(new Ble.OnBleListener() {

            @Override
            public void onReady() {
                mBle.writeChar(mKey.get1040());
            }

            @Override
            public void onReceive(final byte[] receiveDatas) {
                switch (receiveDatas[3]) {
                    case (byte) 4://取开门命令相关设备参数
                        byte[] devRand = new byte[4];//随机数
                        System.arraycopy(receiveDatas, 6, devRand, 0, 4);//取出随机数
                        mBle.writeChar(mKey.get1050(mMac, devRand));
                        break;
                    case (byte) 8:
                    case (byte) 9:
                    case (byte) 5://输出口控制
                        close();
                        if (mOnStateListener != null)
                            mOnStateListener.onState(receiveDatas[5]);
                        break;
                    case (byte) 6://查询外设信息
                        System.arraycopy(receiveDatas, 6, mMac, 0, 9);//MAC_LENGTH=9
                        break;
                    case (byte) 7://设置设备初始密钥 (预留,只是配置工具使用)
                        close();
                        if (mOnStateListener != null)
                            mOnStateListener.onState(300);
                        break;
                    default:
                        close();
                        break;
                }
            }

            @Override
            public void onScanResult(String name, String address, int rssi, byte[] mac) {
                if (mOnScanListener != null) {
                    if (!Utils.containBytesFromDevLst(mac, mScans)) {//是否为扫描记录列表中的设备
                        final BleBean bean = new BleBean(name, address, rssi, mac);
                        mScans.add(bean);
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                mOnScanListener.onScan(mScans, bean);
                            }
                        });
                    }
                }
            }
        });
    }

    public void setOnScanListener(OnScanListener obdll) {
        this.mOnScanListener = obdll;
    }

    public void setOnStateListener(OnStateListener osl) {
        this.mOnStateListener = osl;
    }

    public void close() {
        mBle.close();
        mHandler.removeMessages(1);
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1://超时
                    removeMessages(1);
                    if (mOnStateListener != null)
                        mOnStateListener.onState(200);
                    close();
                    break;
                case 2:
                    scanStop();
                    break;
            }
        }
    };

    public boolean scanStart(int time) {
        scanStop();
        mBle.close();
        mScans.clear();//清空扫描记录列表
        mHandler.removeMessages(2);
        mHandler.sendEmptyMessageDelayed(2, time);
        return mBle.scanStart();
    }

    public void scanStop() {
        mBle.scanStop();
    }

    public void init(byte[] devPassword, byte[] phone, int time) {
        mKey.init(devPassword, phone, time);
    }

    public void verifyData(byte[] macs) {
        mHandler.sendEmptyMessageDelayed(1, mTimeOut);  //超时
        scanStop();
        mMac = macs;
        //校验mac
        String address = Utils.getAddressFromMac(macs, mScans);
        if (address.equals("") && mOnStateListener != null) {
            mOnStateListener.onState(102);//mac不再搜索列表
            return;
        }
        if (mOnStateListener != null) {//设置密码的动作,无需再校验其他数据
            mOnStateListener.onState(mBle.connect(address));
        }
    }

}
3.也就是外部调用的层,方便其他人调用,也可以保证隔离开核心代码,尤其涉及保险柜,门禁,等秘密设备的加密代码,可以完整混淆 
 
 
 
package com.reformer.bles;

import android.content.Context;

public class BleKey {
    private Presenter mPresenter;

    public BleKey(Context ctx) {
        mPresenter = new Presenter(ctx);
    }

    public void scanStart() {
        mPresenter.scanStart(5000);
    }

    //扫描开始
    public void scanStart(int time) {
        mPresenter.scanStart(time);
    }

    public void scanStop() {
        mPresenter.scanStop();
    }

    /**
     * 释放对应楼层权限
     */
    public void ctrlTK(String mac, String phone, int floor) {
//        mPresenter.verifyData(Utils.mac2Bytes(mac, 18), "", 0, Utils.mac2Bytes("0" + phone, 12), 1080, floor);
    }

    /**
     * 释放呼梯控制权限  0是up,1是down
     */
    public void ctrlHT(String mac, String phone, int up) {
//        mPresenter.verifyData(Utils.mac2Bytes(mac, 18), "", 0, Utils.mac2Bytes("0" + phone, 12), 1090, up);
    }

    public void openDoor(String mac, int time) {
        byte[] macs = Utils.stringToBytes(mac, 18);
//        byte[] passwords = Utils.mac2Bytes("3131313131313131" + "D67D67966DA21300", 32);
//        int mOutputTime = time != 0x0a ? time : 0x0a; //设置输出口时间 //不等于默认值时,对其赋值
        mPresenter.init(null,null,time);
        mPresenter.verifyData(macs);
    }

    /**
     * 设置密码
     */
    public void setPassword(String mac, String password) {
//        mPresenter.verifyData(Utils.mac2Bytes(mac, 18), password, 0, null, 1070, 0);
    }

    /**
     * 开门完成监听
     */
    public void setOnStateListener(OnStateListener ocl) {
        mPresenter.setOnStateListener(ocl);
    }

    /**
     * 设备列表实时监听
     */
    public void setOnScanListener(OnScanListener oblc) {
        mPresenter.setOnScanListener(oblc);
    }

}
4.提供外部的扫描监听
package com.reformer.bles;

import java.util.ArrayList;


public interface OnScanListener {

    public void onScan(ArrayList<BleBean> mScans, BleBean bean);
}

5.提供给外部的状态监听
 
 
package com.reformer.bles;

public interface OnStateListener {
    public void onState(int step);
}

6.涉及部分工具类
 
 
package com.reformer.bles;

import android.bluetooth.BluetoothDevice;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;


public class Utils {
    public static String bytes2String(byte[] msg) {
        StringBuffer s = new StringBuffer();
        for (int i = 0; i < msg.length; i++) {
            s.append(Integer.toHexString(((int) msg[i])));
            s.append(",");
        }
        return s.toString();
    }

    /**
     * 5      * byte数组转换成16进制字符串
     * 6      * @param src
     * 7      * @return
     * 8
     */
    public static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder();
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }


    public static byte[] stringToBytes(String outStr, int Length) {
        if (outStr.length() != Length)
            return null;
        int len = outStr.length() / 2;
        byte[] mac = new byte[len];
        String s = null;
        for (int i = 0; i < len; i++) {
            s = outStr.substring(i * 2, i * 2 + 2);
            if (Integer.valueOf(s, 16) > 0x7F) {
                mac[i] = (byte) (Integer.valueOf(s, 16) - 0xFF - 1);
            } else {
                mac[i] = Byte.valueOf(s, 16);
            }
        }
        return mac;
    }

    public static String bytesToString(byte[] mac) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < mac.length; i++) {
            if (mac[i] < 0)
                mac[i] += 256;
            sb.append(padLeft(Integer.toHexString(mac[i]), 2));
        }
        return sb.toString().toUpperCase();
    }

    public static String padLeft(String str, int len) {
        if (str.length() > 2)
            str = str.substring(str.length() - 2);
        String pad = "0000000000000000";
        return len > str.length() && len <= 16 && len >= 0 ? pad.substring(0, len - str.length()) + str : str;
    }

    public static boolean containBytesFromBytesLst(byte[] bytes, ArrayList<byte[]> arrlt) {
        if (arrlt == null)
            return true;
        for (byte[] mac : arrlt) {
            if (Arrays.equals(mac, bytes))
                return true;
        }
        return false;
    }

    public static boolean containBytesFromDevLst(byte[] bytes, ArrayList<BleBean> arrlt) {
        if (arrlt == null)
            return false;
        for (BleBean dev : arrlt) {
            if (Arrays.equals(dev.mac, bytes))
                return true;
        }
        return false;
    }

    public static String getAddressFromMac(byte[] mac, ArrayList<BleBean> list) {
        for (BleBean devContext : list) {
            if (Arrays.equals(mac, devContext.mac))
                return devContext.address;
        }
        return "";
    }

    public static byte[] verifyPhone(String phone) {
        if (phone != null) {
            //校验电话号码
            try {
                Integer.parseInt(phone);
            } catch (Exception e) {
                e.printStackTrace();
                return null;//手机号码不是整数
            }
            StringBuffer sbPhone = new StringBuffer();
            if (phone.length() != 12) {
                if (phone.length() == 11) {
                    sbPhone.append("0").append(phone);
                } else {
                    return null;//手机号码长度不对
                }
            } else {
                sbPhone.append(phone);
            }
            byte[] bPhone = Utils.stringToBytes(sbPhone.toString(), 12);//校验手机号长度
            if (bPhone == null) {
                return null;//105手机号码长度不对
            }
            return bPhone;
        }
        return null;
    }

    public static boolean createBond(Class btClass, BluetoothDevice btDevice) throws Exception {
        Method createBondMethod = btClass.getMethod("createBond");
        Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
        return returnValue.booleanValue();
    }

    public static boolean removeBond(Class btClass, BluetoothDevice btDevice) throws Exception {
        Method removeBondMethod = btClass.getMethod("removeBond");
        Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
        return returnValue.booleanValue();
    }
}


7.通讯的设备对象
 
 
package com.reformer.bles;

public class BleBean {
    public String name;
    public String address;
    public int rssi = 0;
    public byte[] mac = new byte[9];

    public BleBean() {
    }

    public BleBean(String name, String address, int rssi, byte[] mac) {
        this.name = name;
        this.address = address;
        this.rssi = rssi;
        this.mac = mac;
    }

    public String getMacStr() {
        if (mac == null) {
            return null;
        }
        return Utils.bytesToString(mac);
    }

}



猜你喜欢

转载自blog.csdn.net/qq_36017059/article/details/78061706