Android BLE最完整的工具类(扫描/连接/读写/通知设备)

Posted BandaYung

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android BLE最完整的工具类(扫描/连接/读写/通知设备)相关的知识,希望对你有一定的参考价值。

这里只要是android设备与BLE设备的通讯都可以共用,只需要该的就是uuid的值,还有就是ble设备提供商要出文档协议,看看是如何发命令跟接收命令的。

bleUtil工具中,有些地方我感觉还是要提示下

1、characterUUID1 、characterUUID2 、descriptorUUID 这三个是对应的收发命令的,跟找到要操作的BluetoothGattDescriptor(特性)。

在做这个项目的时候ble设备提供商给的文档中uuid,是短的,我实际我打印出来的是长的。其实短的那种是ios来的,而android就不一样咯。可以参考:Android与IOS的UUID的区别

2、sendWorkModel、sendStrength 这两个方法是用来发送命令的,传值都是byte[]数组;而接收命令的话通过receiveData方法进行解析得到的byte[]。这三个方法都是需要根据不同设备协议进行修改,这里只是为了案例展示。总的来说就是收发命令都是byte[]

一丶关键概念:

Generic Attribute Profile (GATT)

通过BLE连接,读写属性类小数据的Profile通用规范。现在所有的BLE应用Profile都是基于GATT的。

Attribute Protocol (ATT)

GATT是基于ATT Protocol的。ATT针对BLE设备做了专门的优化,具体就是在传输过程中使用尽量少的数据。

每个属性都有一个唯一的UUID,属性将以characteristics and services的形式传输。

Characteristic

Characteristic可以理解为一个数据类型,它包括一个value和0至多个对次value的描述(Descriptor)。

Descriptor

对Characteristic的描述,例如范围、计量单位等。

Service

Characteristic的集合。例如一个service叫做“Heart Rate Monitor”,

它可能包含多个Characteristics,其中可能包含一个叫做“heart rate measurement"的Characteristic。

二丶角色和职责:

Android设备与BLE设备交互有两组角色:

中心设备和外围设备(Central vs. peripheral);

GATT server vs. GATT client.

Central vs. peripheral:

中心设备和外围设备的概念针对的是BLE连接本身。

Central角色负责scan advertisement。而peripheral角色负责make advertisement。

GATT server vs. GATT client:

这两种角色取决于BLE连接成功后,两个设备间通信的方式。

举例说明:

现有一个活动追踪的BLE设备和一个支持BLE的Android设备。

Android设备支持Central角色,而BLE设备支持peripheral角色。

创建一个BLE连接需要这两个角色都存在,都仅支持Central角色或者都仅支持peripheral角色则无法建立连接。

当连接建立后,它们之间就需要传输GATT数据。

谁做server,谁做client,则取决于具体数据传输的情况。

例如,如果活动追踪的BLE设备需要向Android设备传输sensor数据,则活动追踪器自然成为了server端;

而如果活动追踪器需要从Android设备获取更新信息,则Android设备作为server端可能更合适。

三丶权限及feature:

和经典蓝牙一样,应用使用蓝牙,需要声明BLUETOOTH权限,

如果需要扫描设备或者操作蓝牙设置,则还需要BLUETOOTH_ADMIN权限:

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

除了蓝牙权限外,如果需要BLE feature则还需要声明uses-feature:

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

按时required为true时,则应用只能在支持BLE的Android设备上安装运行;

required为false时,Android设备均可正常安装运行,需要在代码运行时判断设备是否支持BLE feature

四丶通讯设备的主要步骤有:
设置权限--->打开蓝牙--->扫描设备--->连接设备--->读写数据+通知设备的状态改变--->断开设备

五丶BluetoothGatt的服务层次:
BluetoothGatt--->BluetoothGattService(服务)--->BluetoothGattCharacteristic(特征)--->BluetoothGattDescriptor(特性)

下面的就是重点啦!工具类,工具类,工具类好听的话说三遍

import android.annotation.TargetApi;
import android.app.Activity;
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.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;

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

/**
 * Created by shaolin on 6/17/16.
 */
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class BleUtil 

    private static final String TAG = "BleUtil";
    private static final long SCAN_PERIOD = 10000;

    public static String characterUUID1 = "0000fff2-0000-1000-8000-00805f9b34fb";//APP发送命令
    public static String characterUUID2 = "0000fff1-0000-1000-8000-00805f9b34fb";//BLE用于回复命令
    private static String descriptorUUID = "00002902-0000-1000-8000-00805f9b34fb";//BLE设备特性的UUID

    public static byte[] workModel = 0x02, 0x01;

    private Context mContext;
    private static BleUtil mInstance;

    //作为中央来使用和处理数据;
    private BluetoothGatt mGatt;

    private BluetoothManager manager;
    private BTUtilListener mListener;
    private BluetoothDevice mCurDevice;
    private BluetoothAdapter mBtAdapter;
    private List<BluetoothDevice> listDevice;
    private List<BluetoothGattService> serviceList;//服务
    private List<BluetoothGattCharacteristic> characterList;//特征

    private BluetoothGattService service;
    private BluetoothGattCharacteristic character1;
    private BluetoothGattCharacteristic character2;


    public static synchronized BleUtil getInstance() 
        if (mInstance == null) 
            mInstance = new BleUtil();
        
        return mInstance;
    


    public void setContext(Context context) 
        mContext = context;
        init();
    

    public void init() 
        listDevice = new ArrayList<>();
        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) 
            showToast("BLE不支持此设备!");
            ((Activity) mContext).finish();
        
        manager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
        //注:这里通过getSystemService获取BluetoothManager,
        //再通过BluetoothManager获取BluetoothAdapter。BluetoothManager在Android4.3以上支持(API level 18)。
        if (manager != null) 
            mBtAdapter = manager.getAdapter();
        
        if (mBtAdapter == null || !mBtAdapter.isEnabled()) 
            mBtAdapter.enable();
            /*Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            mContext.startActivity(enableBtIntent);*/
        
    

    //扫描设备的回调
    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() 

        @Override
        public void onLeScan(final BluetoothDevice device, int rssi,
                             byte[] scanRecord) 
            ((Activity) mContext).runOnUiThread(new Runnable() 
                @Override
                public void run() 
                    if (!listDevice.contains(device)) 
                        //不重复添加
                        listDevice.add(device);
                        mListener.onLeScanDevices(listDevice);
                        Log.e(TAG, "device:" + device.toString());
                    
                
            );
        
    ;

    //扫描设备
    public void scanLeDevice(final boolean enable) 
        if (enable) 
            Handler mHandler = new Handler();
            mHandler.postDelayed(new Runnable() 
                @Override
                public void run() 
                    stopScan();
                    Log.e(TAG, "run: stop");
                
            , SCAN_PERIOD);
            startScan();
            Log.e(TAG, "start");
         else 
            stopScan();
            Log.e(TAG, "stop");
        
    

    //开始扫描BLE设备
    private void startScan() 
        mBtAdapter.startLeScan(mLeScanCallback);
        mListener.onLeScanStart();
    

    //停止扫描BLE设备
    private void stopScan() 
        mBtAdapter.stopLeScan(mLeScanCallback);
        mListener.onLeScanStop();
    

    //返回中央的状态和周边提供的数据
    private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() 

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status,
                                            int newState) 
            Log.e(TAG, "onConnectionStateChange");
            switch (newState) 
                case BluetoothProfile.STATE_CONNECTED:
                    Log.e(TAG, "STATE_CONNECTED");
                    mListener.onConnected(mCurDevice);
                    gatt.discoverServices(); //搜索连接设备所支持的service
                    break;
                case BluetoothProfile.STATE_DISCONNECTED:
                    mListener.onDisConnected(mCurDevice);
                    disConnGatt();
                    Log.e(TAG, "STATE_DISCONNECTED");
                    break;
                case BluetoothProfile.STATE_CONNECTING:
                    mListener.onConnecting(mCurDevice);
                    Log.e(TAG, "STATE_CONNECTING");
                    break;
                case BluetoothProfile.STATE_DISCONNECTING:
                    mListener.onDisConnecting(mCurDevice);
                    Log.e(TAG, "STATE_DISCONNECTING");
                    break;
            
            super.onConnectionStateChange(gatt, status, newState);
        

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) 
            Log.d(TAG, "onServicesDiscovered");
            if (status == BluetoothGatt.GATT_SUCCESS) 
                serviceList = gatt.getServices();
                for (int i = 0; i < serviceList.size(); i++) 
                    BluetoothGattService theService = serviceList.get(i);

                    Log.e(TAG, "ServiceName:" + theService.getUuid());
                    characterList = theService.getCharacteristics();
                    for (int j = 0; j < characterList.size(); j++) 
                        String uuid = characterList.get(j).getUuid().toString();
                        Log.e(TAG, "---CharacterName:" + uuid);
                        if (uuid.equals(characterUUID1)) 
                            character1 = characterList.get(j);
                         else if (uuid.equals(characterUUID2)) 
                            character2 = characterList.get(j);
                            setNotification();
                        
                    
                
            
            super.onServicesDiscovered(gatt, status);
        

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) 
            Log.e(TAG, "onCharacteristicRead");
            super.onCharacteristicRead(gatt, characteristic, status);
        

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) 
            Log.e(TAG, "onCharacteristicWrite");
            super.onCharacteristicWrite(gatt, characteristic, status);
        

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) 
            Log.e(TAG, "onCharacteristicChanged");
//            这里是可以监听到设备自身或者手机改变设备的一些数据修改h通知
            receiveData(characteristic);
            super.onCharacteristicChanged(gatt, characteristic);
        
    ;

    //获取设备指定的特征中的特性,其中对其进行监听, setCharacteristicNotification与上面的回调onCharacteristicChanged进行一一搭配
    private void setNotification() 
        mGatt.setCharacteristicNotification(character2, true);
        BluetoothGattDescriptor descriptor = character2.getDescriptor(UUID.fromString(descriptorUUID));
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        mGatt.writeDescriptor(descriptor);
    

    //接收数据,对其进行处理
    private void receiveData(BluetoothGattCharacteristic ch) 
        byte[] bytes = ch.getValue();
        int cmd = bytes[0];
        int agree = bytes[1];
        switch (cmd) 
            case 1:
                mListener.onStrength(agree);
                Log.e(TAG, "手机通知BLE设备强度:" + agree);
                break;
            case 2:
                mListener.onModel(agree);
                Log.e(TAG, "工作模式:" + agree);
                break;
            case 3:
                mListener.onStrength(agree);
                Log.e(TAG, "设备自身通知改变强度:" + agree);
                break;
        
    

    //连接设备
    public void connectLeDevice(int devicePos) 
        mBtAdapter.stopLeScan(mLeScanCallback);
        mCurDevice = listDevice.get(devicePos);
        checkConnGatt();
    

    //发送进入工作模式请求
    public void sendWorkModel() 
        if (character1 != null) 
            character1.setValue(workModel);
            mGatt.writeCharacteristic(character1);
        
    

    //发送强度
    public void sendStrength(int strength) 
        byte[] strengthModel = 0x01, (byte) strength;
        if (character1 != null) 
            character1.setValue(strengthModel);
            mGatt.writeCharacteristic(character1);
        
    

    //检查设备是否连接了
    private void checkConnGatt() 
        if (mGatt == null) 
            mGatt = mCurDevice.connectGatt(mContext, true, mGattCallback);
            mListener.onConnecting(mCurDevice);
         else 
            mGatt.connect();
            mGatt.discoverServices();
        
    

    //  断开设备连接
    private void disConnGatt() 
        if (mGatt != null) 
            mGatt.disconnect();
            mGatt.close();
            mGatt = null;
            listDevice = new ArrayList<>();
            mListener.onLeScanDevices(listDevice);
        
    

    private void showToast(String message) 
        Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
    

    public void setBTUtilListener(BTUtilListener listener) 
        mListener = listener;
    

    public interface BTUtilListener 
        void onLeScanStart(); // 扫描开始

        void onLeScanStop();  // 扫描停止

        void onLeScanDevices(List<BluetoothDevice> listDevice); //扫描得到的设备

        void onConnected(BluetoothDevice mCurDevice); //设备的连接

        void onDisConnected(BluetoothDevice mCurDevice); //设备断开连接

        void onConnecting(BluetoothDevice mCurDevice); //设备连接中

        void onDisConnecting(BluetoothDevice mCurDevice); //设备连接失败

        void onStrength(int strength); //给设备设置强度

        void onModel(int model); //设备模式
    

参考:http://www.myext.cn/android/a_4699.html

重点:  Android与IOS的UUID的区别

 

以上是关于Android BLE最完整的工具类(扫描/连接/读写/通知设备)的主要内容,如果未能解决你的问题,请参考以下文章

Android BLE为啥首次连接蓝牙设备比较慢

Android 4.4:低功耗蓝牙;无需扫描 BLE 设备即可连接

Android-Ble蓝牙开发Demo示例–扫描,连接,发送和接收数据,分包解包(附源码)

Android 10 BLE 连接问题

Android-Ble蓝牙通讯开发–扫描,连接,发送和接收数据,分包解包(附源码)

Android BLE,扫描开始,找到设备但未连接过滤器(ESP32 和三星)