sunsili 发表于 2021-12-6 17:30:56

【Andriod开发】蓝牙BLE(BlueTooth BLE)入门及爬坑指南

本帖最后由 sunsili 于 2021-12-6 17:40 编辑

【Andriod开发】蓝牙BLE(BlueTooth BLE)入门及爬坑指南

前言最近比较忙,两三周没有更新简书了,公司正好在做蓝牙BLE的项目,本来觉得挺简单的东西从网上找了个框架,就咔咔地开始搞,搞完以后才发现里面还有不少坑呢,故而写一篇蓝牙BLE入门及爬坑指南,旨在帮助刚入蓝牙BLE的小伙伴们少走弯路。
注:本文所有的具体代码实现都在文章最后的github上经典蓝牙和蓝牙BLE的区别
说起蓝牙,大家一定听过蓝牙1.0 2.0 3.0 4.0,不过现在已经不再用版本号区分蓝牙了,蓝牙1.0~3.0都是经典蓝牙,在塞班系统就已经开始使用了,确实很经典。有些人一直认为蓝牙4.0就是蓝牙BLE,其实是错误的。因为4.0是双模的,既包括经典蓝牙又包括低能耗蓝牙。经典蓝牙和蓝牙BLE虽然都是蓝牙,但其实还是存在很大区别的。蓝牙BLE相比于经典蓝牙的优点是搜索、连接的速度更快,关键就是BLE(Bluetooth Low Energy)低能耗,缺点呢就是传输的速度慢,传输的数据量也很小,每次只有20个字节。但是蓝牙BLE因为其低能耗的优点,在智能穿戴设备和车载系统上的应用越来越广泛,因此,蓝牙BLE开发已经是我们Android开发不得不去掌握的一门技术了。
蓝牙BLE的简介
蓝牙BLE是在Android4.3系统及以上引入的,但是仅作为中央设备,直到5.0以后才可以既作为中央设备又可以作为周边设备。也就是5.0系统以后,可以手机控制手机了,不过绝大多数的场景手机还是作为中央设备去控制其他的周边设备。Android BLE 使用的蓝牙协议是 GATT 协议。关于这个GATT协议,我就不详细给大家介绍了,放上个链接,感兴趣的可以看一下http://blog.chinaunix.net/uid-21411227-id-5750680.html
Service和Characteristic

Service是服务,Characteristic是特征值。蓝牙里面有多个Service,一个Service里面又包括多个Characteristic,具体的关系可以看图

图中画的比较少,实际上一个蓝牙协议里面包含的Service和Characteristic是比较多的 ,这时候你可能会问,这么多的同名属性用什么来区分呢?答案就是UUID,每个Service或者Characteristic都有一个 128 bit 的UUID来标识。Service可以理解为一个功能集合,而Characteristic比较重要,蓝牙设备正是通过Characteristic来进行设备间的交互的(如读、写、订阅等操作)。小结经典蓝牙和蓝牙BLE虽然都是蓝牙,但是在连接和数据传递上还是存在很大的区别,而蓝牙BLE依靠着其低能耗的特点,逐渐在智能穿戴设备上占有一席之地。蓝牙BLE基于GATT协议传输数据,提供了Serivice和Characteristic进行设备之间的通讯。以上,就是蓝牙BLE的基本概念,下面开始蓝牙BLE的正式开发!

蓝牙BLE正确开发姿势(本文重点)
第一步:声明蓝牙BLE权限

<font color="rgb(153, 153, 153)"><!--声明蓝牙权限--></font>
Android6.0系统以上开启蓝牙还需要定位权限,定位权限属于危险权限,需要动态申请,笔者实现的方法是使用了RxPerssion动态库。 <font color="rgb(153, 153, 153)">/**   * 检查权限   */</font>    <font color="rgb(204, 153, 205)">private</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">checkPermissions</font>() {      <font color="rgb(248, 197, 85)">RxPermissions</font> rxPermissions <font color="rgb(103, 205, 204)">=</font> <font color="rgb(204, 153, 205)">new</font> <font color="rgb(248, 197, 85)">RxPermissions</font>(<font color="rgb(248, 197, 85)">MainActivity</font>.<font color="rgb(204, 153, 205)">this</font>);      rxPermissions.<font color="rgb(240, 141, 73)">request</font>(android.<font color="rgb(248, 197, 85)">Manifest</font>.permission.ACCESS_FINE_LOCATION)                .<font color="rgb(240, 141, 73)">subscribe</font>(<font color="rgb(204, 153, 205)">new</font> io.reactivex.functions.<font color="rgb(248, 197, 85)">Consumer</font><Boolean>() {                  @Override                  <font color="rgb(204, 153, 205)">public</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">accept</font>(<font color="rgb(248, 197, 85)">Boolean</font> aBoolean) <font color="rgb(204, 153, 205)">throws</font> <font color="rgb(248, 197, 85)">Exception</font> {                        <font color="rgb(204, 153, 205)">if</font> (aBoolean) {                            <font color="rgb(153, 153, 153)">// 用户已经同意该权限</font>                            <font color="rgb(240, 141, 73)">scanDevice</font>();                        } <font color="rgb(204, 153, 205)">else</font> {                            <font color="rgb(153, 153, 153)">// 用户拒绝了该权限,并且选中『不再询问』</font>                            <font color="rgb(248, 197, 85)">ToastUtils</font>.<font color="rgb(240, 141, 73)">showLong</font>(<font color="rgb(126, 198, 153)">"用户开启权限后才能使用"</font>);                        }                  }                });    }
第二步:连接蓝牙前需要初始化的工作
mBluetoothManager<font color="rgb(103, 205, 204)">=</font> (BluetoothManager) <font color="rgb(240, 141, 73)">getSystemService</font>(BLUETOOTH_SERVICE);      mBluetoothAdapter<font color="rgb(103, 205, 204)">=</font>mBluetoothManager.<font color="rgb(240, 141, 73)">getAdapter</font>();      <font color="rgb(204, 153, 205)">if</font> (mBluetoothAdapter<font color="rgb(103, 205, 204)">==</font><font color="rgb(204, 153, 205)">null</font><font color="rgb(103, 205, 204)">||</font><font color="rgb(103, 205, 204)">!</font>mBluetoothAdapter.<font color="rgb(240, 141, 73)">isEnabled</font>()){            <font color="rgb(248, 197, 85)">Intent</font> intent<font color="rgb(103, 205, 204)">=</font><font color="rgb(204, 153, 205)">new</font> <font color="rgb(248, 197, 85)">Intent</font>(BluetoothAdapter.ACTION_REQUEST_ENABLE);            <font color="rgb(240, 141, 73)">startActivityForResult</font>(intent,<font color="rgb(240, 141, 73)">0</font>);      }
拿到BluetoothManager,在通过BluetoothManager.getAdapter()拿到BluetoothAdapter,然后判断一下蓝牙是否打开,没打开的话Intent隐式调用打开系统开启蓝牙界面。
第三步:扫描设备
<font color="rgb(153, 153, 153)">/**   * 开始扫描 10秒后自动停止   * */</font>    <font color="rgb(204, 153, 205)">private</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">scanDevice</font>(){      tvSerBindStatus.<font color="rgb(240, 141, 73)">setText</font>(<font color="rgb(126, 198, 153)">"正在搜索"</font>);      isScaning<font color="rgb(103, 205, 204)">=</font><font color="rgb(240, 141, 73)">true</font>;      pbSearchBle.<font color="rgb(240, 141, 73)">setVisibility</font>(<font color="rgb(248, 197, 85)">View</font>.VISIBLE);      mBluetoothAdapter.<font color="rgb(240, 141, 73)">startLeScan</font>(scanCallback);      <font color="rgb(204, 153, 205)">new</font> <font color="rgb(248, 197, 85)">Handler</font>().<font color="rgb(240, 141, 73)">postDelayed</font>(<font color="rgb(204, 153, 205)">new</font> <font color="rgb(248, 197, 85)">Runnable</font>() {            @Override            <font color="rgb(204, 153, 205)">public</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">run</font>() {                <font color="rgb(153, 153, 153)">//结束扫描</font>                mBluetoothAdapter.<font color="rgb(240, 141, 73)">stopLeScan</font>(scanCallback);                <font color="rgb(240, 141, 73)">runOnUiThread</font>(<font color="rgb(204, 153, 205)">new</font> <font color="rgb(248, 197, 85)">Runnable</font>() {                  @Override                  <font color="rgb(204, 153, 205)">public</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">run</font>() {                        isScaning<font color="rgb(103, 205, 204)">=</font><font color="rgb(240, 141, 73)">false</font>;                        pbSearchBle.<font color="rgb(240, 141, 73)">setVisibility</font>(<font color="rgb(248, 197, 85)">View</font>.GONE);                  }                });            }      },<font color="rgb(240, 141, 73)">10000</font>);    }
蓝牙扫描如果不停止,会持续扫描,很消耗资源,一般都是开启10秒左右停止
<font color="rgb(248, 197, 85)">BluetoothAdapter.LeScanCallback</font> scanCallback<font color="rgb(103, 205, 204)">=</font><font color="rgb(204, 153, 205)">new</font> <font color="rgb(248, 197, 85)">BluetoothAdapter.LeScanCallback</font>() {      @<font color="rgb(248, 197, 85)">Override</font>      <font color="rgb(204, 153, 205)">public</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">onLeScan</font>(<font color="rgb(248, 197, 85)">BluetoothDevice</font> device, <font color="rgb(204, 153, 205)">int</font> rssi, <font color="rgb(204, 153, 205)">byte</font>[] scanRecord) {            Log.<font color="rgb(240, 141, 73)">e</font>(TAG, <font color="rgb(126, 198, 153)">"run: scanning..."</font>);            <font color="rgb(204, 153, 205)">if</font> (<font color="rgb(103, 205, 204)">!</font>mDatas.<font color="rgb(240, 141, 73)">contains</font>(device)){                mDatas.<font color="rgb(204, 153, 205)">add</font>(device);                mRssis.<font color="rgb(204, 153, 205)">add</font>(rssi);                mAdapter.<font color="rgb(240, 141, 73)">notifyDataSetChanged</font>();            }      }    };
这里的scanCallback是上一段代码里mBluetoothAdapter.startLeScan(scanCallback)里面的对象,其中onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)里面的参数都很直观,device是设备对象,rssi扫描到的设备强度,scanRecord是扫面记录,没什么卵用。 扫描过的设备仍然会被再次扫描到,因此要加入设备列表之前可以判断一下,如果已经加入过了就不必再次添加了。
看一下搜索的效果图吧



搜索效果图

第三步:连接设备
BluetoothDevice bluetoothDevice<font color="rgb(103, 205, 204)">=</font> mDatas.<font color="rgb(240, 141, 73)">get</font>(position);                  <font color="rgb(153, 153, 153)">//连接设备</font>                  tvSerBindStatus.<font color="rgb(240, 141, 73)">setText</font>(<font color="rgb(126, 198, 153)">"连接中"</font>);                  <font color="rgb(204, 153, 205)">if</font> (Build.VERSION.SDK_INT <font color="rgb(103, 205, 204)">>=</font> Build.VERSION_CODES.M) {                        mBluetoothGatt <font color="rgb(103, 205, 204)">=</font> bluetoothDevice.<font color="rgb(240, 141, 73)">connectGatt</font>(MainActivity.this,                              <font color="rgb(240, 141, 73)">true</font>, gattCallback, TRANSPORT_LE);                  } <font color="rgb(204, 153, 205)">else</font> {                        mBluetoothGatt <font color="rgb(103, 205, 204)">=</font> bluetoothDevice.<font color="rgb(240, 141, 73)">connectGatt</font>(MainActivity.this,                              <font color="rgb(240, 141, 73)">true</font>, gattCallback);                  }
连接这里大家可能已经发现了,判断了一下手机系统,6.0及以上连接设备的方法是bluetoothDevice.connectGatt(MainActivity.this,true, gattCallback, TRANSPORT_LE)。这里就是我遇见的第一个大坑了,我的手机是8.0的系统使用
bluetoothDevice.connectGatt(MainActivity.this, true, gattCallback);总是连接失败,提示status返回133,用了各种方法都不行,后台一查才发现6.0及以上系统的手机要使用bluetoothDevice.connectGatt(MainActivity.this,true, gattCallback, TRANSPORT_LE),其中TRANSPORT_LE参数是设置传输层模式。传输层模式有三种TRANSPORT_AUTO 、TRANSPORT_BREDR 和TRANSPORT_LE。如果不传默认TRANSPORT_AUTO,6.0系统及以上需要使用TRANSPORT_LE这种传输模式,具体为啥,我也不知道,我猜是因为Android6.0及以上系统重新定义了蓝牙BLE的传输模式必须使用TRANSPORT_LE这种方式吧。bluetoothDevice.connectGatt()方法返回的对象BluetoothGatt,这个BluetoothGatt对象非常重要,甚至可以说是最重要的。一般都是单独声明成全局变量来使用的,因为我们设备的读、写和订阅等操作都需要用到这个对象。
<font color="rgb(204, 153, 205)">private</font> <font color="rgb(248, 197, 85)">BluetoothGattCallback</font> gattCallback<font color="rgb(103, 205, 204)">=</font><font color="rgb(204, 153, 205)">new</font> <font color="rgb(248, 197, 85)">BluetoothGattCallback</font>() {      <font color="rgb(153, 153, 153)">/**         * 断开或连接 状态发生变化时调用         * */</font>      @Override      <font color="rgb(204, 153, 205)">public</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">onConnectionStateChange</font>(<font color="rgb(248, 197, 85)">BluetoothGatt</font> gatt, <font color="rgb(204, 153, 205)">int</font> status, <font color="rgb(204, 153, 205)">int</font> newState) {            <font color="rgb(204, 153, 205)">super</font>.<font color="rgb(240, 141, 73)">onConnectionStateChange</font>(gatt, status, newState);            <font color="rgb(248, 197, 85)">Log</font>.<font color="rgb(240, 141, 73)">e</font>(TAG,<font color="rgb(126, 198, 153)">"onConnectionStateChange()"</font>);            <font color="rgb(204, 153, 205)">if</font> (status<font color="rgb(103, 205, 204)">==</font><font color="rgb(248, 197, 85)">BluetoothGatt</font>.GATT_SUCCESS){                <font color="rgb(153, 153, 153)">//连接成功</font>                <font color="rgb(204, 153, 205)">if</font> (newState<font color="rgb(103, 205, 204)">==</font> <font color="rgb(248, 197, 85)">BluetoothGatt</font>.STATE_CONNECTED){                  <font color="rgb(248, 197, 85)">Log</font>.<font color="rgb(240, 141, 73)">e</font>(TAG,<font color="rgb(126, 198, 153)">"连接成功"</font>);                  <font color="rgb(153, 153, 153)">//发现服务</font>                  gatt.<font color="rgb(240, 141, 73)">discoverServices</font>();                }            }<font color="rgb(204, 153, 205)">else</font>{                <font color="rgb(153, 153, 153)">//连接失败</font>                <font color="rgb(248, 197, 85)">Log</font>.<font color="rgb(240, 141, 73)">e</font>(TAG,<font color="rgb(126, 198, 153)">"失败=="</font><font color="rgb(103, 205, 204)">+</font>status);                mBluetoothGatt.<font color="rgb(240, 141, 73)">close</font>();                isConnecting<font color="rgb(103, 205, 204)">=</font><font color="rgb(240, 141, 73)">false</font>;            }      }      <font color="rgb(153, 153, 153)">/**         * 发现设备(真正建立连接)         * */</font>      @Override      <font color="rgb(204, 153, 205)">public</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">onServicesDiscovered</font>(<font color="rgb(248, 197, 85)">BluetoothGatt</font> gatt, <font color="rgb(204, 153, 205)">int</font> status) {            <font color="rgb(204, 153, 205)">super</font>.<font color="rgb(240, 141, 73)">onServicesDiscovered</font>(gatt, status);            <font color="rgb(153, 153, 153)">//直到这里才是真正建立了可通信的连接</font>            isConnecting<font color="rgb(103, 205, 204)">=</font><font color="rgb(240, 141, 73)">false</font>;            <font color="rgb(248, 197, 85)">Log</font>.<font color="rgb(240, 141, 73)">e</font>(TAG,<font color="rgb(126, 198, 153)">"onServicesDiscovered()---建立连接"</font>);            <font color="rgb(153, 153, 153)">//获取初始化服务和特征值</font>            <font color="rgb(240, 141, 73)">initServiceAndChara</font>();            <font color="rgb(153, 153, 153)">//订阅通知</font>            mBluetoothGatt.<font color="rgb(240, 141, 73)">setCharacteristicNotification</font>(mBluetoothGatt                  .<font color="rgb(240, 141, 73)">getService</font>(notify_UUID_service).<font color="rgb(240, 141, 73)">getCharacteristic</font>(notify_UUID_chara),<font color="rgb(240, 141, 73)">true</font>);            <font color="rgb(240, 141, 73)">runOnUiThread</font>(<font color="rgb(204, 153, 205)">new</font> <font color="rgb(248, 197, 85)">Runnable</font>() {                @Override                <font color="rgb(204, 153, 205)">public</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">run</font>() {                  bleListView.<font color="rgb(240, 141, 73)">setVisibility</font>(<font color="rgb(248, 197, 85)">View</font>.GONE);                  operaView.<font color="rgb(240, 141, 73)">setVisibility</font>(<font color="rgb(248, 197, 85)">View</font>.VISIBLE);                  tvSerBindStatus.<font color="rgb(240, 141, 73)">setText</font>(<font color="rgb(126, 198, 153)">"已连接"</font>);                }            });      }      <font color="rgb(153, 153, 153)">/**         * 读操作的回调         * */</font>      @Override      <font color="rgb(204, 153, 205)">public</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">onCharacteristicRead</font>(<font color="rgb(248, 197, 85)">BluetoothGatt</font> gatt, <font color="rgb(248, 197, 85)">BluetoothGattCharacteristic</font> characteristic, <font color="rgb(204, 153, 205)">int</font> status) {            <font color="rgb(204, 153, 205)">super</font>.<font color="rgb(240, 141, 73)">onCharacteristicRead</font>(gatt, characteristic, status);            <font color="rgb(248, 197, 85)">Log</font>.<font color="rgb(240, 141, 73)">e</font>(TAG,<font color="rgb(126, 198, 153)">"onCharacteristicRead()"</font>);      }      <font color="rgb(153, 153, 153)">/**         * 写操作的回调         * */</font>      @Override      <font color="rgb(204, 153, 205)">public</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">onCharacteristicWrite</font>(<font color="rgb(248, 197, 85)">BluetoothGatt</font> gatt, <font color="rgb(248, 197, 85)">BluetoothGattCharacteristic</font> characteristic, <font color="rgb(204, 153, 205)">int</font> status) {            <font color="rgb(204, 153, 205)">super</font>.<font color="rgb(240, 141, 73)">onCharacteristicWrite</font>(gatt, characteristic, status);            <font color="rgb(248, 197, 85)">Log</font>.<font color="rgb(240, 141, 73)">e</font>(TAG,<font color="rgb(126, 198, 153)">"onCharacteristicWrite()status="</font><font color="rgb(103, 205, 204)">+</font>status<font color="rgb(103, 205, 204)">+</font><font color="rgb(126, 198, 153)">",value="</font><font color="rgb(103, 205, 204)">+</font><font color="rgb(248, 197, 85)">HexUtil</font>.<font color="rgb(240, 141, 73)">encodeHexStr</font>(characteristic.<font color="rgb(240, 141, 73)">getValue</font>()));      }      <font color="rgb(153, 153, 153)">/**         * 接收到硬件返回的数据         * */</font>      @Override      <font color="rgb(204, 153, 205)">public</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">onCharacteristicChanged</font>(<font color="rgb(248, 197, 85)">BluetoothGatt</font> gatt, <font color="rgb(248, 197, 85)">BluetoothGattCharacteristic</font> characteristic) {            <font color="rgb(204, 153, 205)">super</font>.<font color="rgb(240, 141, 73)">onCharacteristicChanged</font>(gatt, characteristic);            <font color="rgb(248, 197, 85)">Log</font>.<font color="rgb(240, 141, 73)">e</font>(TAG,<font color="rgb(126, 198, 153)">"onCharacteristicChanged()"</font><font color="rgb(103, 205, 204)">+</font>characteristic.<font color="rgb(240, 141, 73)">getValue</font>());            <font color="rgb(204, 153, 205)">final</font> <font color="rgb(204, 153, 205)">byte</font>[] data<font color="rgb(103, 205, 204)">=</font>characteristic.<font color="rgb(240, 141, 73)">getValue</font>();            <font color="rgb(240, 141, 73)">runOnUiThread</font>(<font color="rgb(204, 153, 205)">new</font> <font color="rgb(248, 197, 85)">Runnable</font>() {                @Override                <font color="rgb(204, 153, 205)">public</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">run</font>() {                  <font color="rgb(240, 141, 73)">addText</font>(tvResponse,<font color="rgb(240, 141, 73)">bytes2hex</font>(data));                }            });      }    };
这一段是连接的回调 这里我只重写了几个比较重要的方法,每个方法都有具体的注释,需要强调的是有些同学重复连接会报133连接失败,这个调用一下mBluetoothGatt.close()就可以解决,还有要注意的就是回调里面的方法不要做耗时的操作,也不要在回调方法里面更新UI,这样有可能会阻塞线程。
第四步:发现服务
<font color="rgb(153, 153, 153)">/**         * 发现设备(真正建立连接)         * */</font>      @Override      <font color="rgb(204, 153, 205)">public</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">onServicesDiscovered</font>(<font color="rgb(248, 197, 85)">BluetoothGatt</font> gatt, <font color="rgb(204, 153, 205)">int</font> status) {            <font color="rgb(204, 153, 205)">super</font>.<font color="rgb(240, 141, 73)">onServicesDiscovered</font>(gatt, status);            <font color="rgb(153, 153, 153)">//直到这里才是真正建立了可通信的连接</font>            isConnecting<font color="rgb(103, 205, 204)">=</font><font color="rgb(240, 141, 73)">false</font>;            <font color="rgb(248, 197, 85)">Log</font>.<font color="rgb(240, 141, 73)">e</font>(TAG,<font color="rgb(126, 198, 153)">"onServicesDiscovered()---建立连接"</font>);            <font color="rgb(153, 153, 153)">//获取初始化服务和特征值</font>            <font color="rgb(240, 141, 73)">initServiceAndChara</font>();            <font color="rgb(153, 153, 153)">//订阅通知</font>            mBluetoothGatt.<font color="rgb(240, 141, 73)">setCharacteristicNotification</font>(mBluetoothGatt                  .<font color="rgb(240, 141, 73)">getService</font>(notify_UUID_service).<font color="rgb(240, 141, 73)">getCharacteristic</font>(notify_UUID_chara),<font color="rgb(240, 141, 73)">true</font>);            <font color="rgb(240, 141, 73)">runOnUiThread</font>(<font color="rgb(204, 153, 205)">new</font> <font color="rgb(248, 197, 85)">Runnable</font>() {                @Override                <font color="rgb(204, 153, 205)">public</font> <font color="rgb(204, 153, 205)">void</font> <font color="rgb(240, 141, 73)">run</font>() {                  bleListView.<font color="rgb(240, 141, 73)">setVisibility</font>(<font color="rgb(248, 197, 85)">View</font>.GONE);                  operaView.<font color="rgb(240, 141, 73)">setVisibility</font>(<font color="rgb(248, 197, 85)">View</font>.VISIBLE);                  tvSerBindStatus.<font color="rgb(240, 141, 73)">setText</font>(<font color="rgb(126, 198, 153)">"已连接"</font>);                }            });      }
直到这里才是建立了真正可通信的连接,下一步就可以进行读写订阅等操作,之前文章中有提到要通过Service和Characteristic特征值来操作,但是如果获取到对应的服务和特征值呢?一般硬件开发工程师会定义好UUID,通知到我们,这个时候我们只需要调用下面的方法就能拿到Service和Characteristic//write_UUID_service和write_UUID_chara是硬件工程师告诉我们的 BluetoothGattService service=mBluetoothGatt.getService(write_UUID_service); BluetoothGattCharacteristic charaWrite=service.getCharacteristic(write_UUID_chara);
当然也会比较坑爹的,就是硬件工程师居然不知道Service和Characteristic的UUID是啥(没错,我就遇见了),这个时候也不要慌,因为我们可以通过Android拿得到对应UUID.



写入以后返回的数据


这里在EditText虽然没有显示,但其实我直接点击默认就输入7B46363941373237323532443741397D 这一串数据,实在懒得打了总结第一次打这么多字有点小累,总结这个地方就不多说了,这里就说点注意事项,在进行蓝牙操作的时候最好每次都延迟200ms再执行,因为蓝牙是线程安全的,当你同时执行多次操作的时候会出现busy的情况导致执行失败,所以这里建议一般都执行一步操作延时一会,这样可以保证操作的成功率,另外就是如果大家入了门以后想要快速的开发的话,建议网上找好轮子,找一个好用的,可以先自己看看实现的源码,当然最好就是自己封装一个。
最后放上我的github地址:https://github.com/kaka10xiaobang/BlueToothBLE

文章来源:蓝牙BLE(BlueTooth BLE)入门及爬坑指南 - 简书 (jianshu.com)

页: [1]
查看完整版本: 【Andriod开发】蓝牙BLE(BlueTooth BLE)入门及爬坑指南