一、何为BLE?

首先得说明什么是低功耗蓝牙BLE,BLE的全称为Bluetooth low energy(或称Blooth LE,BLE),从英文全称便可以知晓其是一种低功耗的蓝牙技术,是蓝牙技术联盟设计和销售的一种个人局域网技术,旨在用于医疗保健、运动健身、信标、安防、家庭娱乐等领域的新兴应用。相较经典蓝牙,低功耗蓝牙旨在保持同等通信范围的同时显著降低功耗和成本。而正因为其低功耗的优点,可以让Android APP可以具有与低功耗要求的BLE设备通信,如近距离传感器、心脏速率监视器、健身设备等


1.2基础术语和概念

在正式开发前,要对基本的蓝牙的术语和概念要有个大致的认识,因为我本人学习的也不长,就是个简单的总结先:


Generic Attribute Profile:简称为GATT,现在的低功耗BLE的连接都是建立在GATT协议之上实现的,蓝牙技术联盟规定了许多低功耗设备的配置文件,配置文件是设备如何在特定的应用程序在工作的规格,而一个设备中可以有多个配置文件。

Generic Access Profile:Profile可以视为一种规范,一个标准的通信协议,它存在于从机中,蓝牙技术联盟规定了一些标准的profile,例如防丢器 ,心率计等。每个profile中会包含多个service,每个service代表从机的一种能力。

Service:服务,在BLE从机中,可以有多个服务,例如:电量信息服务,而Service中又有多个Characteristic特征值,而每个具体的特征值才是BLE通信的重点,例如在1电量信息服务中,当前的电量为80%,所以会通过电量的特征值存在从机的profile里,这样主机就可以通过这个特征值来读取80%这个数据

Characteristic:特征值,ble主从机通信都是通过特征值实现的,类似于标签key,可以通过这个key值来获取信息和数据

UUID:统一识别码,服务和特征值都需要一个唯一的UUID来标识整理,而每个从机都会有一个叫做profile的东西存在,不管是上面的自定义的simpleprofile,还是标准的防丢器profile,他们都是由一些列service组成,然后每个service又包含了多个characteristic,主机和从机之间的通信,均是通过characteristic来实现。


1.3初始化配置

讲完了大概的概念之后便是基本的操作了,以下内容会结合代码和流程图进行展示

Android蓝牙BLE开发_android

1.3.1权限 想要使用BLE开发,就得先获得蓝牙必要的权限,需要先在AndroidManifest.xml中设置权限

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

1 2 如果想声明你的app只为具有BLE的设备提供,在manifest文件中包括:

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

除此之外,如果是Android 6.0以上的手机仅仅是添加以上的蓝牙权限是不足的,这样会造成无法扫描到其他设备,因而还需要添加位置权限:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-feature android:name="android.hardware.location.gps" />

二、是否支持蓝牙

BLE required=true只能是让支持BLE的Android设备上安装运行,不支持的则不行,如果想在Java实现上述功能,可以通过下述代码:

// 手机硬件支持蓝牙
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
    finish();
}

初始化蓝牙适配器

所有的蓝牙活动都需要蓝牙适配器,BluetoothAdapter代表设备本身的蓝牙适配器。整个系统只有一个蓝牙适配器,而且app需要蓝牙适配器与系统交互。下面的代码片段显示了如何得到适配器。


注意该方法使用getSystemService()返回BluetoothManager,然后将其用于获取适配器的一个实例。Android 4.3(API 18)引入BluetoothManager

final BluetoothManager bluetoothManager =(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP)
    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
else
    mBluetoothAdapter = bluetoothManager.getAdapter();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
    mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();

开启蓝牙

要操作蓝牙,必须先在设备中开启蓝牙。如果当前未启用蓝牙,则可以通过触发一个Intent调用系统显示一个对话框来要求用户启用蓝牙权限

// 打开蓝牙权限
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(
        BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

初始化ListView列表适配器

/**
  * @Description: TODO<自定义适配器Adapter,作为listview的适配器>
  */
private class LeDeviceListAdapter extends BaseAdapter {
    private ArrayList<BluetoothMessage> mLeDevices;
    
    private LayoutInflater mInflator;
    
    public LeDeviceListAdapter()
    {
        super();
        //rssis = new ArrayList<Integer>();
        mLeDevices = new ArrayList<BluetoothMessage>();
        mInflator = getLayoutInflater();
     }
    public void addDevice(BluetoothMessage device)
    {
        for (BluetoothMessage mLeDevice : mLeDevices) {
            
            if(mLeDevice.getDevice().getAddress().equals(device.getDevice().getAddress())){
                return;    
            }    
        }    
        mLeDevices.add(device);
        //rssis.add(rssi);
    }
    public BluetoothMessage getDevice(int position)
    {
    	return mLeDevices.get(position);
    }
    public void clear()
    {
        mLeDevices.clear();
        //rssis.clear();
    }
    @Override
    public int getCount()
    {
    	return mLeDevices.size();
    }
	@Override
    public Object getItem(int i)
    {
        return mLeDevices.get(i);
    }
    @Override
    public long getItemId(int i)
    {
        return i;
    }

    /**  
      * 重写getview
      *
      * **/
   @Override
    public View getView(int i, View view, ViewGroup viewGroup)
    {
        // General ListView optimization code.
        // 加载listview每一项的视图
        BluetoothMessage bluetoothMessage = mLeDevices.get(i);
        return view;
    }
}

发现BLE设备(扫描设备)

如果要发现BLE设备,使用startLeScan()方法进行扫描,扫描的话就要传入true执行scanLeDvice(true)方法,然后蓝牙适配器就调用startLeScan()方法进行扫描,LeScanCallback是扫描回调,也就是返回扫描结果。

1.3.6.1扫描结果:

private void scanLeDevice(final boolean enable) {
    if (mBluetoothAdapter == null){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        else {
            BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            mBluetoothAdapter = bluetoothManager.getAdapter();
        }
  	}
    if (mBluetoothLeScanner == null){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
            mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
        }
    if (enable) {
        // Stops scanning after a pre-defined scan period.
        mHandler.postDelayed(new Runnable()
        {
            @Override
            public void run()
            {
                mScanning = false;
                scan_flag = true;
                scan_btn.setText("扫描设备");
                Log.i("SCAN", "stop.....................");
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                    mBluetoothLeScanner.stopScan(mScanCallback);
                else
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
             }
         }, SCAN_PERIOD);
         /* 开始扫描蓝牙设备,带mLeScanCallback 回调函数 */
            Log.i("SCAN", "begin.....................");
            mScanning = true;
            scan_flag = false;
            scan_btn.setText("停止扫描");
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                mBluetoothLeScanner.startScan(mScanCallback);
            else
                mBluetoothAdapter.startLeScan(mLeScanCallback);
     } else {
            Log.i("Stop", "stoping................");
            mScanning = false;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                mBluetoothLeScanner.stopScan(mScanCallback);
            else
                mBluetoothAdapter.stopLeScan(mLeScanCallback);
            scan_flag = true;
      }
}

回调方法: 在这里回家上面的扫描方法的扫描结果回调,也就是传回来。其中在onLeScan方法是重点,蓝牙扫描成功后的结果会返回此方法中,然后就可以处理BluetoothDevice拿到设备信息,最后展示到前面初始化的ListView列表中:

第一个参数device,表示一个远程蓝牙设备,里面有它独有的蓝牙地址Address和Name等,所以后续需要进行连接蓝牙操作也需要用到这里获取的蓝牙Address

第二个参数rssi表示扫描到的设备信号强度,这里应该可以用来判断距离的远近。

第三个参数scanRecord表示远程设备提供的广告记录的内容。

// 这个是官方demo的源码
// 是扫描的Callback的回调,其中的onLeScan方法,蓝牙扫描成功之后会将结果会返回此方法中
// 然后就可以处理BluetoothDevice拿到设备信息 最后展示到前面初始化的listview列表中
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback()
{
    @Override
    public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord)
    {
        // TODO Auto-generated method stub
	    runOnUiThread(new Runnable()
        {
            @Override
            public void run()
            {
                // 讲扫描到设备的信息输出到listview的适配器
                BluetoothMessage bluetoothMessage = new BluetoothMessage(device);
                mleDeviceListAdapter.addDevice(bluetoothMessage);
                mleDeviceListAdapter.notifyDataSetChanged();
             }
         });
    }
};

至此已经完成初始化配置、一些设备的判断逻辑和扫描操作了,如果能成功地扫描到设备并展示到界面上的话,下一步如果用户点击了列表,将进行蓝牙连接和相关的读写操作!

1.4创建BluetoothLeService服务类并初始化蓝牙连接

创建了一个BluetoothLeService服务类并继承了Service,用来完成蓝牙设备的初始化、连接、断开连接、读取特征值、写入特征值、设置特征值变化通知以及获取已连接蓝牙的所有服务等操作

1.4.1创建服务 首先,我们得进行第一步,在onCreate()方法中,执行bindService开启一个服务

这里因为是项目需要,使用了一个虚拟按钮来进行初始化的连接

//蓝牙service,负责后台的蓝牙服务
private static BluetoothLeService mBluetoothLeService;
Intent gattServiceIntent;
/**
     * --------------------------------------------onCreate方法-----------------------------------------------------
     */
@Override
public void onCreate(Bundle savedInstanceState)
{
    ...
        /* 启动蓝牙service */
        gattServiceIntent = new Intent(this, BluetoothLeService.class);
    //模拟按键点击事件触发蓝牙连接
    rev_tv.post(new Runnable() {
        @Override
        public void run() {
            scan_btn.performClick();
        }
    });

    //监听scan_btn
    scan_btn.setOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View v){
            if (scan_flag)
            {
                mleDeviceListAdapter = new LeDeviceListAdapter();
                //lv.setAdapter(mleDeviceListAdapter);
                scanLeDevice(true);
            } else {
                scanLeDevice(false);
                scan_btn.setText("扫描设备");
            }
            if (mScanning) {
                /* 停止扫描设备 */
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                    mBluetoothLeScanner.stopScan(mScanCallback);
                else
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                mScanning = false;
            }
            bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
        }
    });
    ...
}

}

开启服务成功后,便会一样进行服务回调,当服务回调已经成功连接时,便会获取一个BlueToohtLeService的实例,接着就执行蓝牙连接操作:

/* BluetoothLeService绑定的回调函数 */
// 获取BluetoothLeService的实例,进行蓝牙连接操作
// 以下都是Android官方的demo源码
private final ServiceConnection mServiceConnection = new ServiceConnection()
{

    @Override
    public void onServiceConnected(ComponentName componentName,
                                   IBinder service)
    {
        mBluetoothLeService = ((BluetoothLeService.LocalBinder) service)
            .getService();
        if (!mBluetoothLeService.initialize())
        {
            //Log.e(TAG, "Unable to initialize Bluetooth");
            finish();
        }
        mBluetoothLeService.connect(mDeviceAddress);
    }
    @Override
    public void onServiceDisconnected(ComponentName componentName)
    {
        mBluetoothLeService = null;
    }
};

这个时候,需要单独创建一个BlueToothService类,因为BlueToohtLeService类既然是服务类,那它父类肯定是继承于Service

public class BluetoothLeService extends Service {
	...
}

这里的BlueToothService类是BindService服务,用于绑定一个服务。这样当bindService(intent,conn,flags)后,就会绑定一个服务。这样做可以获得这个服务对象本身,而用StartService(intent)的方法只能启动服务。

BindService方法的一般过程:

开启BindService服务

// bindService区别于startService,用于绑定服务,可以获得这个服务对象本身
public class LocalBinder extends Binder {
    public BluetoothLeService getService()
    {
        return BluetoothLeService.this;
    }
}
// onBind()是使用bindService开启的服务才会有回调的一个方法
// onBind()方法给MainActivity返回了BluetoothLeService实例
// 用于方便MainActivity后续的连接和读写操作
@Override
public IBinder onBind(Intent intent)
{
    return mBinder;
}

关闭BindService,关闭蓝牙

当服务调用unbindService时,服务的生命周期将会进入onUnbind()方法,接着执行了关闭蓝牙的方法

@Override
public boolean onUnbind(Intent intent)
{
    close();
    return super.onUnbind(intent);
}

private final IBinder mBinder = new LocalBinder();

初始化蓝牙

这个方法是BlueToohtLeService服务类创建之后在MainActivity通过拿到BlueToohtLeService实例调用的,也是官方的源码

/* service 中蓝牙初始化 */
public boolean initialize()
{
    // For API level 18 and above, get a reference to BluetoothAdapter
    // through
    // BluetoothManager.
    if (mBluetoothManager == null)
    {   //获取系统的蓝牙管理器
        //使用 getSystemService(java.lang.String)与 BLUETOOTH_SERVICE创建一个 BluetoothManager
        // 然后调用 getAdapter()以获得 BluetoothAdapter
        mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        if (mBluetoothManager == null)
        {
            Log.e(TAG, "Unable to initialize BluetoothManager.");
            return false;
        }
    }

    //BluetoothManager 的变量调用 getAdapter()以获得 BluetoothAdapter 进而对整体蓝牙进行管理
    mBluetoothAdapter = mBluetoothManager.getAdapter();
    if (mBluetoothAdapter == null)
    {
        Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
        return false;
    }

    return true;
}

执行connect()和connectGatt

connect()和connectGatt都是连接BLE设备的方法,但二者用法不同

connectGatt是BluetoothDevice类下的方法,功能是向BLE设备发起连接,然后得到一个BluetoothGatt类型的返回值,利用这个返回值可以进行下一步操作。

connect是BluetoothGatt类下的方法,功能是重新连接。如果BLE设备和APP已经连接过,但是因为设备超出了蓝牙的连接范围而断掉,那么当设备重新回到连接范围内时,可以通过connect()重新连接

// 连接远程蓝牙
    public boolean connect(final String address)
    {
        // 适配器为空或者地址为空就会提示
        if (mBluetoothAdapter == null || address == null)
        {
            Log.w(TAG,
                    "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }

        // Previously connected device. Try to reconnect.
        if (mBluetoothDeviceAddress != null
                && address.equals(mBluetoothDeviceAddress)
                && mBluetoothGatt != null)
        {
            Log.d(TAG,
                    "Trying to use an existing mBluetoothGatt for connection.");
            //mBluetoothGatt.connect()表示连接回远程设备
            //在连接断开后,此方法的功能在于“重新连接到远程设备”
            //如果设备曾经连接过,但目前不在范围内
            //则一旦设备回到范围内,则可以通过connect重新连接。
            if (mBluetoothGatt.connect())//连接蓝牙,其实就是调用BluetoothGatt的连接方法
            {
                mConnectionState = STATE_CONNECTING;
                return true;
            } else
            {
                return false;
            }
        }
        /* 获取远端的蓝牙设备 */
        //mBluetoothAdapter.getRemoteDevice
        //蓝牙适配器通过调用getRemoteDevice()方法获取给定的蓝牙硬件地址的BluetoothDevice对象
        //有效的蓝牙地址必须是6个字节,即使没有找到设备,也得返回有效的6个字节,当然,估计就是6个0
        final BluetoothDevice device = mBluetoothAdapter
                .getRemoteDevice(address);
        if (device == null)
        {
            Log.w(TAG, "Device not found.  Unable to connect.");
            return false;
        }
        // We want to directly connect to the device, so we are setting the
        // autoConnect
        // parameter to false.
        /* 调用device中的connectGatt连接到远程设备 */
        //connectGatt是BluetoothDevice类下的方法
        //功能是向BLE设备发起连接,然后得到一个BluetoothGatt类型的返回值,利用这个返回值可以进行下一步操作
        //connectGatt方法往往是和BluetoothGatt类的connect方法一起使用
        //两个方法的运行逻辑是:
        //先使用connectGatt方法发起连接,连接状态的改变会回调callback对象中的onConnectionStateChange
        // (需要自己定义一个BluetoothGattCallBack对象并重写onConnectionStateChange)
        // 并返回一个BluetoothGatt对象,这时BluetoothGatt已经实例化,下一次连接可以调用connect重新连接。
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        Log.d(TAG, "Trying to create a new connection.");
        mBluetoothDeviceAddress = address;
        mConnectionState = STATE_CONNECTING;
        System.out.println("device.getBondState==" + device.getBondState());
        return true;
    }

取消连接

/**
     * @Title: disconnect
     * @Description: TODO(取消蓝牙连接)
     * @return void
     * @throws
     */
public void disconnect()
{
    if (mBluetoothAdapter == null || mBluetoothGatt == null)
    {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    mBluetoothGatt.disconnect();

}

BluetoothGattCallback 回调 这个回调十分重要,主要对BluetoothGatt的蓝牙连接、断开、读、写、特征值变化等的回调监听,然后我们可以将这些回调信息通过广播机制传播回给广播监听器

/* 连接远程设备的回调函数 */
// BluetoothGattCallback是一个抽象类,目的是用于实现 BluetoothGatt的回调
// 用于将结果传递给用户,例如连接状态等,以及任何进一步对GATT客户端的操作
// 因为BluetoothGattCallback是一个抽象类,因此需要对里面的方法进行重写
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback()
{
    // 连接状态变化时回调,用来检测蓝牙是否连接成功与否,成功失败两种情况的操作在这里设置
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status,
                                        int newState)
    {
        String intentAction;
        if (newState == BluetoothProfile.STATE_CONNECTED)//连接成功
        {   // 连接成功后的操作
            intentAction = ACTION_GATT_CONNECTED;   // 连接外设成功(GATT服务端)
            mConnectionState = STATE_CONNECTED;     // 设备连接完毕
            /* 通过广播更新连接状态 */
            broadcastUpdate(intentAction);          // 广播更新,查看是否有数据更新
            Log.i(TAG, "Connected to GATT server.");
            // Attempts to discover services after successful connection.
            Log.i(TAG, "Attempting to start service discovery:"
                  + mBluetoothGatt.discoverServices());

        } else if (newState == BluetoothProfile.STATE_DISCONNECTED)//连接失败
        {   //连接失败后的操作
            intentAction = ACTION_GATT_DISCONNECTED;// 连接外设失败(GATT服务端)
            mConnectionState = STATE_DISCONNECTED;  // 设备无法连接
            Log.i(TAG, "Disconnected from GATT server.");
            broadcastUpdate(intentAction);
        }
    }
    /*
         * 重写onServicesDiscovered,发现蓝牙服务 会在蓝牙连接的时候调用
         *
         * */
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status)
    {   // GATT_SUCCESS表示GATT操作完成
        if (status == BluetoothGatt.GATT_SUCCESS)//发现蓝牙服务成功
        {
            broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            Log.i(TAG, "--onServicesDiscovered called--");
        } else
        {
            Log.w(TAG, "onServicesDiscovered received: " + status);
            System.out.println("onServicesDiscovered received: " + status);
        }
    }
    /*
         * 特征值的读写
         * */
    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,
                                     BluetoothGattCharacteristic characteristic, int status)
    {
        if (status == BluetoothGatt.GATT_SUCCESS)
        {
            Log.i(TAG, "--onCharacteristicRead called--");
            //从特征值读取数据
            // characteristic是特征值,而特征值是用16bit或者128bit,16bit是官方认证过的,128bit是可以自定义的
            // 这里的两步操作第一步获得二进制的特征值,第二步将其变成字符串
            byte[] sucString = characteristic.getValue();
            String string = new String(sucString);
            //将数据通过广播到Ble_Activity
            //broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        }

    }
    /*
         * 特征值的改变
         * */
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt,
                                        BluetoothGattCharacteristic characteristic)
    {
        System.out.println("++++++++++++++++");
        broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        // ACTION_DATA_AVAILABLE: 接受来自设备的数据,可以通过读或通知操作获得
    }
    /*
         * 特征值的写
         * */
    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt,
                                      BluetoothGattCharacteristic characteristic, int status) {

        super.onCharacteristicWrite(gatt, characteristic, status);
        if (status == BluetoothGatt.GATT_SUCCESS) {//发送完成
            mSendState = true;
            Log.d("AppRun"+getClass().getSimpleName(),"发送完成");
        }
    }
    /*
         * 读描述值
         * */
    @Override
    public void onDescriptorRead(BluetoothGatt gatt,
                                 BluetoothGattDescriptor descriptor, int status)
    {
        // TODO Auto-generated method stub
        // super.onDescriptorRead(gatt, descriptor, status);
        Log.w(TAG, "----onDescriptorRead status: " + status);
        byte[] desc = descriptor.getValue();
        if (desc != null)
        {
            Log.w(TAG, "----onDescriptorRead value: " + new String(desc));
        }

    }
    /*
         * 写描述值
         * */
    @Override
    public void onDescriptorWrite(BluetoothGatt gatt,
                                  BluetoothGattDescriptor descriptor, int status)
    {
        // TODO Auto-generated method stub
        // super.onDescriptorWrite(gatt, descriptor, status);
        Log.w(TAG, "--onDescriptorWrite--: " + status);
    }
    /*
         * 读写蓝牙信号值
         * */
    // Rssi是蓝牙的接受信号强度
    @Override
    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status)
    {
        // TODO Auto-generated method stub
        // super.onReadRemoteRssi(gatt, rssi, status);
        Log.w(TAG, "--onReadRemoteRssi--: " + status);
        broadcastUpdate(ACTION_DATA_AVAILABLE, rssi);
    }

    @Override
    public void onReliableWriteCompleted(BluetoothGatt gatt, int status)
    {
        // TODO Auto-generated method stub
        // super.onReliableWriteCompleted(gatt, status);
        Log.w(TAG, "--onReliableWriteCompleted--: " + status);
    }

};

设置特征值变化通知 为了让手机APP接收蓝牙设备发送的数据,必须要设置这个setCharacteristicNotification()方法,这个十分重要。否则,手机APP将无法接受蓝牙设备的数据

MainActivity代码

mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

BluetoothLeService类的代码

/**
     * @Title: setCharacteristicNotification
     * @Description: TODO(设置特征值通变化通知)
     * @param @param characteristic(特征值)
     * @param @param enabled (使能)
     * @return void
     * @throws
     */
public void setCharacteristicNotification(
    BluetoothGattCharacteristic characteristic, boolean enabled)
{
    if (mBluetoothAdapter == null || mBluetoothGatt == null)
    {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

    BluetoothGattDescriptor clientConfig = characteristic
        .getDescriptor(UUID
                       .fromString("00002902-0000-1000-8000-00805f9b34fb"));
    // 其中UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"是HC-08蓝牙设备的监听UUID
    if (enabled)
    {
        clientConfig
            .setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    } else
    {
        clientConfig
            .setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
    }
    mBluetoothGatt.writeDescriptor(clientConfig);
}