Android 蓝牙开发(BLE)封装

声明

本文主要讲解BLE(低功耗蓝牙4.0以上)的使用和封装,为了UI层方便拿取数据展示,统一对蓝牙搜索、连接、数据交互、蓝牙协议等封装为lib。

一.BLE简介

为什么要学习蓝牙技术,蓝牙作为一种成熟、低功耗无线通信技术的先锋,在可穿戴设备领域中扮演着越来越重要的作用。

BLE分为三部分:ServiceCharacteristicDescriptor。这三部分都是使用UUID来作为唯一标识符加以区分。一个BLE终端可以包含多个Service,一个Service可以包含多个Characteristic,而一个Characteristic包含一个value和多个Descriptor,一个Descriptor只包含一个valueUUID格式为:0000ffe1-0000-1000-8000-00805f9b34fb

二.蓝牙前期简单介绍

1.添加权限

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" /> 

如果你不确定你的app使用的设备是否支持低功耗蓝牙,但又想让支持的设备使用低功耗蓝牙,把标志位改为false,同时去代码中判断设备是否支持BLE:

// 是否支持蓝牙低功耗广播(4.3+)
if (!getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            finish()//此处可以根据特定需求去改变处理
            return false;
}

2.蓝牙适配器BluetoothAdapter

有两种获取方式:

1).BluetoothManager bluetoothManager =(BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
  BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();

2).BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 

此处需要做非空判断,为null则表示设备不支持蓝牙。

BluetoothAdapter方法描述:
  • isEnabled() 判断系统蓝牙是否打开
  • disable() 无弹窗提示关闭系统蓝牙
  • enable() 无弹窗提示打开系统蓝牙(此操作有点不友好!)
  • startDiscovery() 开始搜索设备 —–适合经典蓝牙和低功耗蓝牙两种
  • cancelDiscovery() 取消搜索设备
  • startLeScan() 开始搜索设备 —–适合扫描低功耗蓝牙,但是在api21以上被标记废弃使用
  • stopLeScan() 停止搜索设备
  • startScan() 开始搜索设备 —–api21以上扫描低功耗蓝牙,通过bluetoothAdapter.getBluetoothLeScanner()方法获取
  • stopScan() 停止搜索设备
  • stopScan() 停止搜索设备

说明:startDiscovery()方法在大多数手机上是可以同时发现经典蓝牙和低功耗蓝牙(BLE)的,但是startDiscovery()的回调无法返回BLE的广播,所以无法通过广播识别设备,而且startDiscovery()扫描BLE效率比startLeScan()低很多。因此需要根据具体的需求去做适配,才能更高效的搜寻蓝牙。PS: startLeScan()和startScan()有重载方法可以指定规则,参数去搜索。

3.蓝牙协议BluetoothGatt

获取方式:

BluetoothGatt mBluetoothGatt = device.connectGatt(getContext(), false, mGattCallback);//创建连接
BluetoothGatt方法描述:
  • connect() 连接远程设备
  • discoverServices() 搜索连接设备所支持的service
  • disconnect() 断开与远程设备的GATT连接
  • close() 关闭GATT Client端,释放资源
  • readCharacteristic(characteristic) 在指定的characteristic特征端口中读取数据
  • writeDescriptor(characteristic) 在指定的characteristic特征端口中写入数据
  • setCharacteristicNotification(characteristic, enabled) 设置当指定characteristic特征端口值变化时,是否发出通知返回
  • readRemoteRssi() 读取当前连接设备信号值,返回数值为负,值越小信号越弱。
  • getServices() 获取远程设备所支持的services
  • getDevice() 获取已连接的设备信息

三.蓝牙正式封装

与BLE蓝牙交互分为三步骤:搜索、连接、读写数据。BLE蓝牙无需配对即可连接

1.搜索、连接蓝牙

在搜索、连接蓝牙之前,首先对官方提供的BluetoothLeService进行一层封装,此类无非就是对BluetoothGatt协议的的获取和使用(连接设备,断开连接,发现服务,读写设备等操作)。本文采用单列模式和接口回调方式进行解耦分离、数据交互,源码实现方式为服务+广播的形式。PS:ble蓝牙官方demo网上资源还是比较多的,如果没有可以在下方留言。

package com.csym.bluetoothlib;

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 com.orhanobut.logger.Logger;

import java.util.List;

/**
 * 蓝牙服务管理类
 * Created by ${zhoupeng} on 2016/8/10.
 */
public class BluetoothLeService {

    private BluetoothManager mBluetoothManager;//用来获取蓝牙适配器
    private BluetoothAdapter mBluetoothAdapter;//蓝牙适配器,处理系统蓝牙是否打开,搜索设备
    private BluetoothGatt mBluetoothGatt;//发现蓝牙服务,根据特征值处理数据交互
    private BluetoothDevice mBluetoothDevice = null;//蓝牙设备

    private static BluetoothLeService INSTANCE = null;//单列模式
    private static Context mContext;//上下文

    /**
     * 是否连接
     */
    private boolean isConnected = false;

    public boolean isConnected() {
        return isConnected;
    }

    public void setConnected(boolean connected) {
        isConnected = connected;
    }

    /**
     * 获取上下文
     *
     * @return context
     */
    private Context getContext() {
        return mContext;
    }

    private BluetoothLeService() {
        boolean value = initialize();
        if (!value) Logger.e("蓝牙适配器adapter初始化失败!!!");
    }

    /**
     * 单列模式,确保唯一性
     *
     * @param context 上下文
     * @return 对象
     */
    public static BluetoothLeService getInstance(Context context) {
        mContext = context;
        if (INSTANCE == null) {
            synchronized (BluetoothLeService.class) {
                if (INSTANCE == null) {
                    INSTANCE = new BluetoothLeService();
                }
            }
        }
        return INSTANCE;
    }

    /**
     * 初始化bluetooth adapter
     *
     * @return 为null返回false
     */
    private boolean initialize() {
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) getContext().getSystemService(Context.BLUETOOTH_SERVICE);
        }
        if (mBluetoothManager == null) return false;
        mBluetoothAdapter = mBluetoothManager.getAdapter();
        return mBluetoothAdapter != null;
    }

    /**
     * 启用或者禁用通知\标志返回特性 true, if the requested notification status was set successfully
     *
     * @param characteristic 蓝牙特征对象
     * @param enabled        是否允许
     * @return 设置成功或失败
     */
    public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
        return !(mBluetoothAdapter == null || mBluetoothGatt == null)
                && mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
    }

    public boolean writeDescriptor(BluetoothGattDescriptor bluetoothGattDescriptor) {
        return !(bluetoothGattDescriptor == null || mBluetoothGatt == null)
                && mBluetoothGatt.writeDescriptor(bluetoothGattDescriptor);
    }

    /**
     * 发现数据通道服务
     *
     * @return true or false
     */
    public boolean discoverServices() {
        return !(!isConnected() || mBluetoothGatt == null)
                && mBluetoothGatt.discoverServices();
    }

    /**
     * 读取蓝牙数据
     *
     * @param characteristic 蓝牙特征值
     * @return true or false
     */
    public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
        return !(mBluetoothAdapter == null || mBluetoothGatt == null)
                && mBluetoothGatt.readCharacteristic(characteristic);
    }

    /**
     * 往设备中写入数据
     *
     * @param characteristic 蓝牙特征值
     * @return true or false
     */
    public boolean writeCharecteristic(BluetoothGattCharacteristic characteristic) {
        return !(mBluetoothAdapter == null || mBluetoothGatt == null)
                && mBluetoothGatt.writeCharacteristic(characteristic);
    }

    /**
     * 获取gatt服务列表
     *
     * @return 远程设备提供的gatt服务列表(功能通道)
     */
    public List getSupportedGattServices() {
        if (mBluetoothGatt == null) return null;
        return mBluetoothGatt.getServices();
    }

    /**
     * 获取RSSI值
     *
     * @return null false
     */
    public boolean getRssiValue() {
        return mBluetoothGatt != null
                && mBluetoothGatt.readRemoteRssi();
    }

    /**
     * 获取蓝牙设备对象
     *
     * @return BluetoothDevice
     */
    public BluetoothDevice getDevice() {
        return mBluetoothDevice;
    }

    /**
     * 判断设备是否连接
     *
     * @param address 设备地址
     * @return true 已连接
     */
    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {
            Logger.e("蓝牙适配器没有初始化获取mac地址未指明。");
            return false;
        }
        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            Logger.e("蓝牙设备没有发现,无法连接。");
            return false;
        }
        close();//每次连接之前先释放掉原来的资源
        mBluetoothGatt = device.connectGatt(getContext(), false, mGattCallback);//创建新的连接
        return true;
    }

    /**
     * 断开连接
     */
    public void disconnect() {
        if (mBluetoothGatt != null) mBluetoothGatt.disconnect();
    }

    /**
     * 释放资源
     */
    public void close() {
        if (mBluetoothGatt != null) {
            mBluetoothGatt.close();
            mBluetoothDevice = null;
        }
    }

    /**
     * 蓝牙协议回调
     */
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        /**
         * 连接状态
         */
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (mStateChangeListener == null) return;
            if (newState == BluetoothProfile.STATE_CONNECTED) {// 连接状态
                mStateChangeListener.connected(gatt.getDevice());
                setConnected(true);
                mBluetoothDevice = gatt.getDevice();
                discoverServices();//设备连接成功,查找服务!
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {// 断开连接
                close();
                mStateChangeListener.disconnected();
                setConnected(false);
                mBluetoothDevice = null;
            }
        }

        /**
         * 是否发现服务
         */
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                if (mCharacteristicListener != null)
                    mCharacteristicListener.onServicesDiscovered();
            } else {
                Logger.e("蓝牙通信服务回调失败:status=" + status);
            }
        }

        /**
         * 读操作回调
         */
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if (mCharacteristicListener != null) {
                mCharacteristicListener.onCharacteristicRead(gatt, characteristic, status);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            if (mCharacteristicListener != null) {
                mCharacteristicListener.onCharacteristicChanged(gatt, characteristic);
            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if (mCharacteristicListener != null) {
                mCharacteristicListener.onCharacteristicWrite(gatt, characteristic, status);
            }
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
            Logger.e("onDescriptorWrite: status=" + status + "   uuid=" + descriptor.getUuid().toString());
        }

        /**
         * 信号强度
         */
        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
            if (mCharacteristicListener != null) {
                mCharacteristicListener.onReadRemoteRssi(gatt, rssi, status);
            }
        }
    };


    /**
     * 连接状态接口
     */
    public interface OnConnectionStateChangeListener {
        void connected(BluetoothDevice device);

        void disconnected();
    }

    /**
     * 发现服务、数据读写操作接口
     */
    public interface OnCharacteristicListener {
        void onServicesDiscovered();

        void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status);

        void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);

        void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status);

        void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status);
    }

    private OnConnectionStateChangeListener mStateChangeListener = null;
    private OnCharacteristicListener mCharacteristicListener = null;

    public void setOnConnectionStateChangeListener(OnConnectionStateChangeListener listener) {
        this.mStateChangeListener = listener;
    }

    public void setOnCharacteristicListener(OnCharacteristicListener listener) {
        this.mCharacteristicListener = listener;
    }

}

定义IBluzScanHelper接口,同时在接口中定义搜索和连接蓝牙需要使用到的方法,统一化管理。

package com.csym.bluetoothlib.helper;

import android.bluetooth.BluetoothDevice;
import android.content.Context;

/**
 * 蓝牙设备搜索连接管理类
 * Created by ${zhoupeng} on 2016/8/9.
 */
public interface IBluzScanHelper {

    /**
     * 注册蓝牙回调广播
     *
     * @param context 上下文
     */
    void registBroadcast(Context context);

    /**
     * 注销蓝牙回调广播
     *
     * @param context 上下文
     */
    void unregistBroadcast(Context context);

    /**
     * 获取设备使能信息
     */
    boolean isEnabled();

    /**
     * 关闭蓝牙
     */
    boolean disable();

    /**
     * 打开蓝牙
     */
    boolean enable();

    /**
     * 打开蓝牙提示框
     */
    void openBluetooth();

    /**
     * 开始扫描蓝牙设备
     */
    void startDiscovery();

    /**
     * 取消扫描
     */
    void cancelDiscovery();

    /**
     * 连接蓝牙设备,当前应用所需的Profile
     */
    boolean connect(BluetoothDevice device);

    /**
     * 断开全部连接
     */
    void disconnect();


    /**
     * 设置搜索设备回调监听
     */
    void addOnDiscoveryListener(OnDiscoveryListener listener);

    void removeOnDiscoveryListener(OnDiscoveryListener listener);

    /**
     * 设置连接回调监听
     */
    void addOnConnectionListener(OnConnectionListener listener);

    void removeOnConnectionListener(OnConnectionListener listener);

    /**
     * 断开数据连接,释放资源
     */
    void release();

    /**
     * 获取当前已连接的设备
     */
    BluetoothDevice getConnectedDevice();


    /**
     * 查找蓝牙回调接口
     */
    interface OnDiscoveryListener {
        /**
         * 找到设备
         */
        void onFound(BluetoothDevice device);

        /**
         * 搜索结束
         */
        void onDiscoveryFinished();
    }

    /**
     * 连接回调接口
     */
    interface OnConnectionListener {
        /**
         * 已连接
         */
        void onConnected(BluetoothDevice device);

        /**
         * 已断开连接
         */
        void onDisconnected(BluetoothDevice device);
    }
}

建立BluzScanHelper帮助类,对IBluzScanHelper接口中的方法具体实现,同时提供内部接口将搜索到的蓝牙设备和连接状态提供出去。以下主要讲解下搜索方法的具体实现:
(1) api21以下ble搜索方法为:

bluetoothAdapter.startLeScan(mLeScanCallback);

此方法如果不主动停止将会一直搜索,比较消耗cpu,因此在开启搜索的同时开启一个定时为10秒的定时器去关闭搜索,如果有需要可以在上文提到的onDiscoveryFinished()搜索结束回调接口当中间隔一段时间继续开启搜索,直到连接上你需要的设备为止。

(2) api21以上ble搜索方法为:

//使用之前需要判断当前系统蓝牙是否可用
if (!bluetoothAdapter.isEnabled()) return;
mLeScanner = bluetoothAdapter.getBluetoothLeScanner();
            mLeScanner.startScan(mBleScanCallback);

在不同的版本上需使用不同的方法去搜索,如果在低版本上使用高版本的方法,会造成程序的奔溃。以上两种方法在查找到需要的设备同时都应该取消搜索,释放此资源。

以下为BluzScanHelper类:

package com.csym.bluetoothlib.helper;


import android.annotation.TargetApi;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.support.annotation.RequiresApi;

import com.csym.bluetoothlib.BluetoothLeService;
import com.csym.bluetoothlib.utils.SharePreferenceUtils;
import com.orhanobut.logger.Logger;

import java.util.ArrayList;
import java.util.List;

/**
 * 蓝牙设备搜索连接管理类
 * Created by ${zhoupeng} on 2016/8/9.
 */
public class BluzScanHelper implements IBluzScanHelper, BluetoothLeService.OnConnectionStateChangeListener {

    private BluetoothLeService mBluetoothLeService;//蓝牙服务类
    private BluetoothAdapter bluetoothAdapter;//蓝牙适配器
    private BluetoothLeScanner mLeScanner = null;//api 5.0版本以上新的低功耗蓝牙搜索类
    private BleScanCallback mBleScanCallback = null;//api 5.0版本以上搜索回调接口

    private static IBluzScanHelper INSTANCE = null;
    private Activity mActivity = null;

    public Activity getActivity() {
        return mActivity;
    }

    private boolean isDiscovery = false;

    private boolean isDiscovery() {
        return isDiscovery;
    }

    private void setDiscovery(boolean discovery) {
        isDiscovery = discovery;
    }

    private static final int MSG_CONNECTED = 1;//连接
    private static final int MSG_DISCONNECTED = 2;//未连接
    /**
     * 10秒后停止查找搜索.
     */
    private static final int SCAN_PERIOD = 10 * 1000;

    private SharePreferenceUtils preferenceUtils = null;

    /**
     * 使用handler返回主线程,避免UI层直接操作而导致的奔溃
     */
    private Handler mHandler = new Handler(new Callback() {

        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_CONNECTED://连接
                    BluetoothDevice device = (BluetoothDevice) msg.obj;
                    if (connectionListenerList != null && connectionListenerList.size() > 0 && device != null) {
                        for (int i = 0; i < connectionListenerList.size(); i++) {
                            connectionListenerList.get(i).onConnected(device);
                        }
                    }
                    break;
                case MSG_DISCONNECTED://未连接
                    for (int i = 0; i < connectionListenerList.size(); i++) {
                        connectionListenerList.get(i).onDisconnected(null);
                    }
                    break;
            }
            return false;
        }
    });

    public static IBluzScanHelper getInstance(Activity activity) {
        if (INSTANCE == null) {
            synchronized (BluetoothLeService.class) {
                if (INSTANCE == null) {
                    INSTANCE = new BluzScanHelper(activity);
                }
            }
        }
        return INSTANCE;
    }

    private BluzScanHelper(Activity activity) {
        this.mActivity = activity;
        //蓝牙服务
        mBluetoothLeService = BluetoothLeService.getInstance(activity);
        mBluetoothLeService.setOnConnectionStateChangeListener(this);
        //蓝牙适配器
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter != null
                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mLeScanner = bluetoothAdapter.getBluetoothLeScanner();
            mBleScanCallback = new BleScanCallback();
        }
        //SharePreference保存蓝牙断开行为是主动断开还是自动断开
        preferenceUtils = SharePreferenceUtils.getInstance(activity);
    }

    @Override
    public void registBroadcast(Context context) {
        Logger.e("注册监听系统蓝牙状态变化广播 ");
        IntentFilter filter = new IntentFilter();
        //蓝牙状态改变action
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        //蓝牙断开action
        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
        context.registerReceiver(receiver, filter);
    }

    @Override
    public void unregistBroadcast(Context context) {
        try {
            if (receiver != null) context.unregisterReceiver(receiver);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
    }

    @Override
    public boolean isEnabled() {
        return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
    }

    @Override
    public boolean disable() {
        return bluetoothAdapter != null && bluetoothAdapter.disable();
    }

    @Override
    public boolean enable() {
        return bluetoothAdapter != null && bluetoothAdapter.enable();
    }

    @Override
    public void openBluetooth() {
        //打开蓝牙提示框
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        getActivity().startActivityForResult(enableBtIntent, 100);
    }

    @Override
    public void startDiscovery() {
        if (bluetoothAdapter == null) {
            Logger.e("开始搜索蓝牙失败,蓝牙适配器为null");
            return;
        }
        Logger.e("开始搜索蓝牙 isDiscovering=" + bluetoothAdapter.isDiscovering());
        //正在查找中,不做处理
        if (isDiscovery()) return;
        setDiscovery(true);
        //判断版本号,如果api版本号大于5.0则使用最新的方法搜素
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            scanLeDevice(true);
        } else {
            if (!bluetoothAdapter.isEnabled()) return;
            mLeScanner = bluetoothAdapter.getBluetoothLeScanner();
            mLeScanner.startScan(mBleScanCallback);
        }
    }

    @Override
    public void cancelDiscovery() {
        if (bluetoothAdapter == null) {
            Logger.e("取消搜索蓝牙失败,蓝牙适配器为null");
            return;
        }
//        if (!isDiscovery()) {
//            Logger.e("取消搜索蓝牙失败,当前状态为取消状态!");
//            return;
//        }
        setDiscovery(false);//复位正在查找标志位
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            scanLeDevice(false);
        } else {
            stopLeScan();
        }
        Logger.e("取消搜索蓝牙 isDiscovering=" + bluetoothAdapter.isDiscovering());
    }

    /**
     * 低功耗蓝牙开始查找蓝牙
     *
     * @param enable 是否开始查找
     */
    private void scanLeDevice(boolean enable) {
        if (enable) {
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    stopLeDevice();
                }
            }, SCAN_PERIOD);
            bluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            stopLeDevice();
        }
    }

    /**
     * 低功耗蓝牙停止查找
     */
    private void stopLeDevice() {
        bluetoothAdapter.stopLeScan(mLeScanCallback);
        for (int i = 0; i < discoveryListenerList.size(); i++) {
            discoveryListenerList.get(i).onDiscoveryFinished();
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void stopLeScan() {
        if (!bluetoothAdapter.isEnabled()) return;
        mLeScanner = bluetoothAdapter.getBluetoothLeScanner();
        mLeScanner.stopScan(mBleScanCallback);
        for (int i = 0; i < discoveryListenerList.size(); i++) {
            discoveryListenerList.get(i).onDiscoveryFinished();
        }
    }

    @Override
    public boolean connect(BluetoothDevice device) {
        if (device == null || mBluetoothLeService == null) return false;
        cancelDiscovery();//取消搜索
        return mBluetoothLeService.connect(device.getAddress());
    }

    @Override
    public void disconnect() {
        if (mBluetoothLeService != null) {
            mBluetoothLeService.disconnect();
            preferenceUtils.save(SharePreferenceUtils.SHARE_REFRESH_IS_MANUAL, true);
        }
    }

    @Override
    public void connected(BluetoothDevice device) {
        Message message = new Message();
        message.obj = device;
        message.what = MSG_CONNECTED;
        mHandler.sendMessage(message);
        preferenceUtils.save(SharePreferenceUtils.SHARE_REFRESH_IS_MANUAL, false);
        cancelDiscovery();//连接成功后取消搜索
    }

    @Override
    public void disconnected() {
        mHandler.sendEmptyMessage(MSG_DISCONNECTED);
    }

    @Override
    public void release() {
        if (mBluetoothLeService != null) {
            mBluetoothLeService.disconnect();
            mBluetoothLeService.close();
        }
    }

    @Override
    public BluetoothDevice getConnectedDevice() {
        return mBluetoothLeService.getDevice();
    }

    private List discoveryListenerList = new ArrayList<>();//查找蓝牙回调接口集合
    private List connectionListenerList = new ArrayList<>();//连接蓝牙回调接口集合

    @Override
    public void addOnDiscoveryListener(OnDiscoveryListener listener) {
        if (!discoveryListenerList.contains(listener))
            discoveryListenerList.add(listener);
    }

    @Override
    public void removeOnDiscoveryListener(OnDiscoveryListener listener) {
        discoveryListenerList.remove(listener);
    }

    @Override
    public void addOnConnectionListener(OnConnectionListener listener) {
        connectionListenerList.add(listener);
    }

    @Override
    public void removeOnConnectionListener(OnConnectionListener listener) {
        connectionListenerList.remove(listener);
    }

    /**
     * 查找低功耗蓝牙接口回调
     */
    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {

        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
            //注意在此方法中不要过多的操作
            if (device != null && discoveryListenerList != null) {
                for (int i = 0; i < discoveryListenerList.size(); i++) {
                    discoveryListenerList.get(i).onFound(device);
                }
            }
        }
    };

    /**
     * api21+低功耗蓝牙接口回调,以下回调的方法可以根据需求去做相应的操作
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private class BleScanCallback extends ScanCallback {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            if (result == null) return;
            BluetoothDevice device = result.getDevice();
            if (device != null && discoveryListenerList != null) {
                for (int i = 0; i < discoveryListenerList.size(); i++) {
                    discoveryListenerList.get(i).onFound(device);
                }
            }
        }

        @Override
        public void onBatchScanResults(List results) {
            super.onBatchScanResults(results);
        }

        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);
        }
    }

    /**
     * 查找蓝牙广播回调
     */
    private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            Logger.e("蓝牙广播回调 action=" + action);
            if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_OFF) {//关闭系统蓝牙
                setDiscovery(false);
                Logger.e("系统蓝牙断开!!");
                boolean isEnable = enable();
                if (!isEnable) openBluetooth();
            } else if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_ON) {//系统蓝牙打开
                setDiscovery(false);
                Logger.e("系统蓝牙打开!!");
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        startDiscovery();
                    }
                }, 500);
            }
        }
    };
}

2.数据读写、转换管理

新建BluzManager类,主要功能:

(1)管理蓝牙服务协议。与设备蓝牙连接成功后,调用discoverServices()方法回调蓝牙服务协议,再根据底层提供的协议对蓝牙服务进行过滤,过滤出需要的、可用的特征服务和特征值并缓存。
(2)读写、通知数据回调处理。在对设备进行读、写、通知操作时,会有相应的数据回调回来。示例当中创建了解析工厂模式对通知回来的数据进行了对应的处理,并以广播的形式发送出去。
(3)通信管理。在此类中创建方法分别实现底层操作指令的拼接,上层如果有需要,直接调用相关方法即可。

package com.csym.bluetoothlib.manager;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.content.Context;

import com.csym.bluetoothlib.BluetoothAttributes;
import com.csym.bluetoothlib.BluetoothLeService;
import com.csym.bluetoothlib.decoder.IDecoder;
import com.csym.bluetoothlib.factory.DecoderFactory;
import com.csym.bluetoothlib.thread.WriteThread;
import com.csym.bluetoothlib.utils.CRCUtils;
import com.csym.bluetoothlib.utils.HexStringUtils;
import com.orhanobut.logger.Logger;

import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import static android.bluetooth.BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;

/**
 * 设备管理类
 * Created by ${zhoupeng} on 2016/8/11.
 */
public class BluzManager implements BluetoothLeService.OnCharacteristicListener {

    private static BluzManager INSTANCE = null;
    private Context mContext;
    private BluetoothLeService mBluetoothLeService;

    /**
     * 缓存连接设备的蓝牙特征值和uuid
     */
    private HashMap mWriteHashMap = new HashMap<>();

    private WriteThread mWriteThread;
    private DecoderFactory mDecoderFactory;

    private Context getContext() {
        return mContext;
    }

    /**
     * 蓝牙信号强度回调接口
     */
    private OnReadRemoteRssiListener listener;

    public interface OnReadRemoteRssiListener {
        void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status);
    }

    public void setOnReadRemoteRssiListener(OnReadRemoteRssiListener listener) {
        this.listener = listener;
    }

    private BluzManager(Context context) {
        mContext = context;
        initialize();
    }

    /**
     * 单列模式,确保唯一性
     *
     * @param context 上下文
     * @return BluzManager蓝牙管理类
     */
    public static BluzManager getInstance(Context context) {
        if (INSTANCE == null) {
            synchronized (BluzManager.class) {
                if (INSTANCE == null) {
                    INSTANCE = new BluzManager(context);
                }
            }
        }
        return INSTANCE;
    }

    /**
     * 初始化相关数据
     */
    private void initialize() {
        //初始化蓝牙服务类
        mBluetoothLeService = BluetoothLeService.getInstance(getContext());
        mBluetoothLeService.setOnCharacteristicListener(this);
        //初始化解析工厂
        if (mDecoderFactory == null) mDecoderFactory = new DecoderFactory(getContext());
        //初始化写线程
        if (mWriteThread == null) mWriteThread = new WriteThread(mBluetoothLeService);
    }

    /**
     * 获取蓝牙信号强度,只能单次获取
     *
     * @return boolean
     */
    public boolean getRssiValue() {
        return mBluetoothLeService.getRssiValue();
    }

    /**
     * 清楚所有缓存数据
     */
    private void clearHashMap() {
        mWriteHashMap.clear();
    }

    /**
     * 蓝牙服务发现回调
     */
    @Override
    public void onServicesDiscovered() {
        clearHashMap();
        List list = mBluetoothLeService.getSupportedGattServices();
        for (BluetoothGattService bluetoothGattService : list) {
            UUID uuid = bluetoothGattService.getUuid();
            Logger.d("蓝牙服务回调 uuid=" + uuid.toString());
            //根据底层提供的可用的特征服务uuid过滤出可用的服务以及特征值
            if (BluetoothAttributes.UUID_CHARACTERISTICS_SERVICE.equalsIgnoreCase(uuid.toString())) {
                List characteristicList = bluetoothGattService.getCharacteristics();
                for (BluetoothGattCharacteristic characteristic : characteristicList) {
                    //根据服务特征中的属性区分是可读、可写、通知。
                    int properties = characteristic.getProperties();
                    //拥有写权限的uuid放入集合中缓存起来,在需要使用的时候拿取出来。
                    if ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0) {
                        mWriteHashMap.put(characteristic.getUuid(), characteristic);
                    }
                    //打开通知权限,以下BluetoothAttributes.UUID_RESPONSE_2902为举例说明,具体根据底层给过来的文档去修改
                    if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
                        if (mBluetoothLeService.setCharacteristicNotification(characteristic, true)) {
                            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                                    UUID.fromString(BluetoothAttributes.UUID_RESPONSE_2902));
                            descriptor.setValue(ENABLE_NOTIFICATION_VALUE);
                            mBluetoothLeService.writeDescriptor(descriptor);
                        }
                    }
                }
            }
        }
        //如果缓存特征服务为空,表示服务回调失败了,可以尝试断开连接或者关闭系统蓝牙重新去连接。
        if (mWriteHashMap.size() == 0) {
            BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
            if (bluetoothAdapter != null) bluetoothAdapter.disable();
            return;
        }
        //初始化写数据线程特征服务
        mWriteThread.initData(mWriteHashMap);
        //获取服务成功则允许数据写入,,断开连接则重置
        mWriteThread.setServiceCallback(true);
        Logger.e("蓝牙服务回调:mWriteHashMap=" + mWriteHashMap);
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        Logger.e("蓝牙数据读取回调 status=" + status);
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        BluetoothDevice device = mBluetoothLeService.getDevice();//当前连接的蓝牙对象
        if (characteristic == null || characteristic.getValue() == null || device == null) return;
        String data = HexStringUtils.bytesToHexString(characteristic.getValue());//将byte类型数据转换为16进制
        Logger.e("蓝牙数据通知回调 uuid==" + characteristic.getUuid().toString() + ",data=" + data);
        // 工厂模式分别去解析数据
        IDecoder decoder = mDecoderFactory.getDecoder(data);
        if (decoder == null) return;
        decoder.decode(data);
    }

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        Logger.e("蓝牙数据写入回调 status=" + status);
        if (status == BluetoothGatt.GATT_SUCCESS) {//数据写入成功,写入下一条指令
            mWriteThread.threadNotify(true);
        } else {
            mWriteThread.threadNotify(false);
        }
    }

    @Override
    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
        if (listener != null) listener.onReadRemoteRssi(gatt, rssi, status);
    }

    /**
     * 示例:时间同步
     * 根据实际需求去改变
     */
    public void timeSynch() {
        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH) + 1;
        int dayMon = calendar.get(Calendar.DAY_OF_MONTH);
        int week = calendar.get(Calendar.DAY_OF_WEEK);
        int dayWeek = week == 1 ? 6 : week - 2;
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);
        int second = calendar.get(Calendar.SECOND);
        byte[] content = new byte[10];
        content[0] = (byte) HexStringUtils.hexStringToAlgorism("c0");//头字段
        content[1] = (byte) 0x0a;//数据长度
        content[2] = HexStringUtils.str2Bcd((year % 100) + "")[0];//年 L
        content[3] = HexStringUtils.str2Bcd((year / 100) + "")[0];//年 H
        content[4] = HexStringUtils.str2Bcd(month + "")[0];//月
        content[5] = HexStringUtils.str2Bcd(dayMon + "")[0];//月--日
        content[6] = HexStringUtils.str2Bcd(dayWeek + "")[0];//星期---日
        content[7] = HexStringUtils.str2Bcd(hour + "")[0];//小时
        content[8] = HexStringUtils.str2Bcd(minute + "")[0]; //分钟
        content[9] = HexStringUtils.str2Bcd(second + "")[0];//秒
        mWriteThread.startWrite(getCrc16(content));
    }

    /**
     * 添加校验码
     *
     * @param content byte[]
     * @return 具备校验码的byte[]
     */
    private byte[] getCrc16(byte[] content) {
        int crc = CRCUtils.crc16_ccitt(content, content.length);//crc校验码
        byte crcL = (byte) (crc & 0xff);
        byte crcH = (byte) ((crc >> 8) & 0xff);
        byte[] bytes = new byte[content.length + 2];
        System.arraycopy(content, 0, bytes, 0, content.length);
        bytes[content.length] = crcL;
        bytes[content.length + 1] = crcH;
        return bytes;
    }

    /**
     * 停止写入数据
     */
    public void stopWrite() {
        if (mWriteThread != null) {
            mWriteThread.stopWrite();
            mWriteThread = null;
        }
    }

    /**
     * 蓝牙断开,清除剩余指令
     */
    public void clearOrder() {
        if (mWriteThread != null) {
            mWriteThread.setServiceCallback(false);
            mWriteThread.clearAll();
        }
    }

}

四.蓝牙注意事项(未完善)

  1. 设备断开时,应该调用BluetoothGatt.close()方法去释放资源,而不仅仅去BluetoothGatt.disconnect()。建议不要去缓存BluetoothGatt,每次重新连接应当创建新的BluetoothGatt,因为在有些手机上重连后发现不了蓝牙服务。
  2. 蓝牙操作不能过于频繁,否则很容易出现断开连接的情况,建议间隔200-500毫秒操作一次(当然也可以根据实际情况去调整此时间)。
  3. 对于设备的操作最好放入子线程中去处理,此通信涉及到跨进程,很可能出现超时异常(ANR)。

源码下载

上文所用到的数据转换,线程发送命令等工具类在源码中都有体现。

你可能感兴趣的:(蓝牙开发)