之前的文章简单实现了使用传统蓝牙进行通讯的DEMO,说是最简单其实只是夸张的写法~毕竟标题党横行,我们也得学学点~至少没有UC震惊部那么夸张。

然后,本来是要写Android开发之BlueTooth--最简单的Andorid低功耗(BLE)蓝牙通讯Demo的,写了一半发现BLE简单不起来,所以分两篇来写BLE相关的应用。因此大家可能发现,你下篇的编写时间别上篇还早?这只是假象~CSDN的发布时间是建立草稿的时间,我也是醉了。实际发布时间比上一篇迟将近一周...

上一篇已经简单的写了一下关于GATT服务端的一些知识以及搭建。毕竟这个部分很少要我们做的,所以网络上这方面的资源相对较少。通常外围设备都是有固定产品的,用不着我们开发,例如心率器,血压器等等各种感应器,直接把相关数据格式以及UUID给我们在手机上进行就可以了。但是了解一下也没什么坏处。那么这篇就是简单实现如何用手机连接这些产品获取到对应数据了。

服务端

上一篇文章我们写了一个服务端,这篇就是基于那个服务端程序来建立的连接。所以大家有必要先看一下上一篇,否则这里对应数据的UUID怎么来都不知道。还需要说明的一点是,上一篇我们仅仅成功开启了服务,但是数据更新的逻辑没写。假如他是一个心率传感器,那么我们心率改变时,理应要更新数据,并通知客户端,所以上一篇还缺少了一段代码:

 //4.模拟数据更新private void updateCharacteristic() {if (mBluetoothGattServer == null) {return;}final Handler updateHandler = new Handler();updateHandler.postDelayed(new Runnable() {@Overridepublic void run() {for (BluetoothDevice d : mRegisteredDevices) {BluetoothGattCharacteristic newCharacteristic = mBluetoothGattServer.getService(TIME_SERVICE).getCharacteristic(CURRENT_TIME);byte[] data = ("数据更新" + System.currentTimeMillis()).getBytes();newCharacteristic.setValue(data);mBluetoothGattServer.notifyCharacteristicChanged(d, newCharacteristic, false);}updateHandler.postDelayed(this, 5000);}}, 5000);//5s更新一次outputLog("数据模拟更新启动",MSG_TYPE_NORMAL);}

当广播成功后就启动这个数据更新的函数就行了。接下来开始编写客户端了。编写之前先说明几点:

BLE客户端和传统蓝牙代码上的差异

1.获取适配器

传统蓝牙是这么获取适配器的:

if (mBluetoothAdapter == null) {mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();}

BLE蓝牙是这样的:

if (mBluetoothAdapter == null) {/**这里和传统的蓝牙有什么区别呢?*/final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);mBluetoothAdapter = bluetoothManager.getAdapter();}

到底这算不算区别?反正官方文档这么写,其实这两个互换没感觉到有什么变化,反正都是获取到适配器。具体有没有影响,我还没得测试。有兴趣的试试看,然后告诉我哈~

2.搜索回调

传统蓝牙是需要通过广播来获取搜索结果的:

 private void registerRec() {//3.注册蓝牙广播mReceiver = new BlueToothStateReceiver();IntentFilter filter = new IntentFilter();filter.addAction(BluetoothDevice.ACTION_FOUND);//搜索到蓝牙filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜索结束registerReceiver(mReceiver, filter);}class BlueToothStateReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(MainActivity.this, "触发广播", Toast.LENGTH_SHORT).show();String action = intent.getAction();switch (action) {case BluetoothDevice.ACTION_FOUND:BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);Toast.makeText(MainActivity.this, "找到设备" + device.getName(), Toast.LENGTH_SHORT).show();if (mRvAdapter != null) {mRvAdapter.addDevice(device);}break;case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:mMessageAdapter.addMessage("搜索结束");break;}}}

BLE蓝牙是直接以回调方法进行监听的:

 private MyLeScanCallback1 mLeScanCallback1 = new MyLeScanCallback1();private void scanBleDevices1(boolean enable) {if (enable) {outputLog("正在搜索设备");outputLog("当前搜索回调:callback 1");// Stops scanning after a pre-defined scan period.mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mScanning = false;mBluetoothAdapter.stopLeScan(mLeScanCallback1);outputLog("停止搜索");}}, 10000);//设置10秒超时mScanning = true;mBluetoothAdapter.startLeScan(mLeScanCallback1);} else {outputLog("停止搜索");mScanning = false;mBluetoothAdapter.stopLeScan(mLeScanCallback1);}}class MyLeScanCallback1 implements BluetoothAdapter.LeScanCallback {@Overridepublic void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {mDeviceAdapter.addDevice(bluetoothDevice);}}

3.连接方式

传统蓝牙连接别的设备:

 public void run() {if (mDevice != null) {try {//获取套接字BluetoothSocket temp = mDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(BT_UUID));//mDevice.createRfcommSocketToServiceRecord(UUID.fromString(BT_UUID));//sdk 2.3以下使用mSocket = temp;//发起连接请求if (mSocket != null) {mSocket.connect();}sendHandlerMsg("连接 " + mDevice.getName() + "成功!");//获取输入输出流btIs = mSocket.getInputStream();btOs = mSocket.getOutputStream();//通讯-接收消息BufferedReader reader = new BufferedReader(new InputStreamReader(btIs, "UTF-8"));String content = null;while (canRecv) {content = reader.readLine();sendHandlerMsg("收到消息:" + content);}} catch (IOException e) {e.printStackTrace();sendHandlerMsg("错误:" + e.getMessage());} finally {try {if (mSocket != null) {mSocket.close();}//btIs.close();//两个输出流都依赖socket,关闭socket即可//btOs.close();} catch (IOException e) {e.printStackTrace();sendHandlerMsg("错误:" + e.getMessage());}}}}

BLE连接服务端:

private void connectGatt(BluetoothDevice device) {outputLog("正在连接到:"+device.getName());//新建一个链接mBluetoothGatt = device.connectGatt(BleMainActivity.this, false, mBluetoothGattCallback);//mBluetoothGatt.connect();//如果是断开重连//mBluetoothGatt.disconnect();//断开当前连接}

然后在回调里处理连接成功或者失败的逻辑。

4.通讯方式

传统蓝牙是获取到socket只有以类似于TCP的方式进行通讯。

BLE蓝牙则是通过读写特性来进行通讯。

5.其他

代码上的区别我目前也就总结了这几点,至于其他只能在更详细的开发中慢慢发现了。

搜索回调的方式居然过时了?

相信不少人在按照谷歌文档或者其他翻译文档的博客来写demo得时候发现:


不对啊!我明明按照官方的来,怎么会过时!这官方文档到底自己没有进行更新我就不知道了,反正他在API>=21时,确实过时了。那我们能用什么方法来代替他?我们又应该怎么修改?这里涉及到一个新的类:BluetoothLeScanner 蓝牙扫描器。先看他的获取实例代码

if (mBluetoothAdapter == null) {final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);mBluetoothAdapter = bluetoothManager.getAdapter();//mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//如果调用scanBleDevices2(),请加上这句。 Call requires API level 21if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {mBleScanner = mBluetoothAdapter.getBluetoothLeScanner();}}

然后把把搜索方法修改如下:

这么修改就不会提示过时了,而且第二种回调提供更丰富的处理结果。

开始搭建客户端(中央设备)

说了这么多还没开始真正的写客户端呢,那现在开始一步一步来写一个简单的客户端。

1.首先看看需要准备东西有哪些:

private static final String TAG = "BleMainActivity";public static final int REQUEST_BT_ENABLE_CODE = 200;private BluetoothAdapter mBluetoothAdapter;//蓝牙适配器private MyLeScanCallback1 mLeScanCallback1;//搜索回调1private MyLeScanCallback2 mLeScanCallback2;//搜索回调2private BluetoothGatt mBluetoothGatt;//GATT客户端private BluetoothLeScanner mBleScanner;//BLE扫描器private boolean mScanning;//是否正在搜索private RecyclerView devicesView, msgsView;//设备列表、日志列表private RvAdapter mDeviceAdapter;//设备搜索结果适配器private MsgAdapter mMessageAdapter;//日志适配器private List<BluetoothGattService> mServiceList;//服务列表private SimpleDateFormat mDateFormat;private boolean isCallback1=true;

2.前面几步和传统蓝牙差不多,首先打开蓝牙,最好能判断是否支持BLE

private void openBT() {if (mBluetoothAdapter == null) {/**这里和传统的蓝牙有什么区别呢?*/final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);mBluetoothAdapter = bluetoothManager.getAdapter();//mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//如果调用scanBleDevices2(),请加上这句。 Call requires API level 21if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {mBleScanner = mBluetoothAdapter.getBluetoothLeScanner();}}//1.设备不支持蓝牙,结束应用if (mBluetoothAdapter == null) {finish();return;}//2.判断蓝牙是否打开if (!mBluetoothAdapter.enable()) {//没打开请求打开Intent btEnable = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(btEnable, REQUEST_BT_ENABLE_CODE);outputLog("正在打开蓝牙");}}

2.然后是搜索设备,注意,搜索是非常耗电的,最好是搜索到设备之后立马结束搜索,或者10s后停止。

API<21

 private void scanBleDevices1(boolean enable) {if (enable) {outputLog("正在搜索设备");outputLog("当前搜索回调:callback 1");// Stops scanning after a pre-defined scan period.mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mScanning = false;mBluetoothAdapter.stopLeScan(mLeScanCallback1);outputLog("停止搜索");}}, 10000);//设置10秒超时mScanning = true;mBluetoothAdapter.startLeScan(mLeScanCallback1);} else {outputLog("停止搜索");mScanning = false;mBluetoothAdapter.stopLeScan(mLeScanCallback1);}}

API>=21

private void scanBleDevices2(boolean enable) {if (mBleScanner == null) {outputLog("搜索器初始化失败");return;}if (enable) {outputLog("当前搜索回调:callback 2");outputLog("正在搜索设备");// Stops scanning after a pre-defined scan period.mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mScanning = false;mBleScanner.stopScan(mLeScanCallback2);outputLog("停止搜索");}}, 10000);//设置10秒超时mScanning = true;mBleScanner.startScan(mLeScanCallback2);} else {mScanning = false;mBleScanner.stopScan(mLeScanCallback2);outputLog("停止搜索");}}

3.处理搜索结果

API<21

 class MyLeScanCallback1 implements BluetoothAdapter.LeScanCallback {@Overridepublic void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {mDeviceAdapter.addDevice(bluetoothDevice);}}

API>=21

 class MyLeScanCallback2 extends ScanCallback {@Overridepublic void onScanResult(int callbackType, ScanResult result) {super.onScanResult(callbackType, result);mDeviceAdapter.addDevice(result.getDevice());}@Overridepublic void onScanFailed(int errorCode) {super.onScanFailed(errorCode);}@Overridepublic void onBatchScanResults(List<ScanResult> results) {super.onBatchScanResults(results);//批量结果}}

4.在搜索结果,点击链接某个设备

  private void connectGatt(BluetoothDevice device) {outputLog("正在连接到:"+device.getName());//新建一个链接mBluetoothGatt = device.connectGatt(BleMainActivity.this, false, mBluetoothGattCallback);//mBluetoothGatt.connect();//如果是断开重连//mBluetoothGatt.disconnect();//断开当前连接}

5.处理发起连接后回调

 private BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {@Overridepublic void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {super.onPhyUpdate(gatt, txPhy, rxPhy, status);}@Overridepublic void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {super.onPhyRead(gatt, txPhy, rxPhy, status);}@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {super.onConnectionStateChange(gatt, status, newState);if (newState == BluetoothProfile.STATE_CONNECTED) {//成功连接outputLog("连接蓝牙服务成功");mBluetoothGatt.discoverServices();//搜索服务器中的包含服务outputLog("搜索外围服务");} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {//断开连接outputLog("断开蓝牙服务");}}@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {super.onServicesDiscovered(gatt, status);if (status == BluetoothGatt.GATT_SUCCESS) {outputLog("成功搜索到服务");//找到服务//获取服务列表mServiceList = gatt.getServices();//设置特性更改通知setCharacteristicNotification();} else {outputLog("服务搜索失败");Log.w(TAG, "onServicesDiscovered received: " + status);}}@Overridepublic void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicRead(gatt, characteristic, status);if (status == BluetoothGatt.GATT_SUCCESS) {//读取特性//characteristic.getValue();outputLog("读取到特性:"+new String(characteristic.getValue()));}}@Overridepublic void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicWrite(gatt, characteristic, status);if (status == BluetoothGatt.GATT_SUCCESS) {//反写成功}}@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {super.onCharacteristicChanged(gatt, characteristic);//服务端特性改变 读取新数据outputLog("接收到特性变化:"+new String(characteristic.getValue()));gatt.readCharacteristic(characteristic);}@Overridepublic void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {super.onDescriptorRead(gatt, descriptor, status);if (status == BluetoothGatt.GATT_SUCCESS) {//参考特性的处理}}@Overridepublic void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {super.onDescriptorWrite(gatt, descriptor, status);if (status == BluetoothGatt.GATT_SUCCESS) {//反写成功}}@Overridepublic void onReliableWriteCompleted(BluetoothGatt gatt, int status) {super.onReliableWriteCompleted(gatt, status);}@Overridepublic void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {super.onReadRemoteRssi(gatt, rssi, status);}@Overridepublic void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {super.onMtuChanged(gatt, mtu, status);}};

6.设置监听某个特性的的变化

 /*** 找到自己想要获取的特性,并设置更改通知* 例如,服务里面提供了心率检测和血压监测,我现在只想获取心率*/private void setCharacteristicNotification() {if (mBluetoothAdapter == null || mBluetoothGatt == null) {Log.w(TAG, "BluetoothAdapter not initialized");return;}//找到对应服务BluetoothGattCharacteristic characteristic = null;for (BluetoothGattService s : mServiceList) {if (s.getUuid().equals(GattServerActivity.TIME_SERVICE)) {//找到对应服务的特性List<BluetoothGattCharacteristic> cList = s.getCharacteristics();for (BluetoothGattCharacteristic c : cList) {if (c.getUuid().equals(GattServerActivity.CURRENT_TIME)) {//找出需要通知改变的特性characteristic = c;}}}}if (characteristic == null) {return;//服务中不包含我们需要获取的特性}//启动通知:BLE应用程序通常在设备上的特定特性发生变化时要求收到通知//一旦为特性启用通知,如果远程设备上的特性发生变化,则触发回调onCharacteristicChanged()mBluetoothGatt.setCharacteristicNotification(characteristic, true);outputLog("开启特性变化通知成功");// This is specific to Heart Rate Measurement.//这里要对应我服务端所使用的UUID,详情请查看上一篇博客//更改特性描述if (GattServerActivity.CURRENT_TIME.equals(characteristic.getUuid())) {BluetoothGattDescriptor descriptor = characteristic.getDescriptor(GattServerActivity.CLIENT_CONFIG);descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);mBluetoothGatt.writeDescriptor(descriptor);}//然后读一下这个特性mBluetoothGatt.readCharacteristic(characteristic);//会触发回调,这里就到回调中处理}

7.其他操作,读写特性、读写描述等

//写特性,其他操作类似,直接使用mBluetoothGatt调用对应的方法即可private boolean writeCharacteristic(BluetoothGattCharacteristic characteristic){if (mBluetoothGatt!=null){return mBluetoothGatt.writeCharacteristic(characteristic);}return false;}

8.关闭连接

 /*** 关闭蓝牙或者退出应用别忘了关闭客户端哦*/private void close() {if (mBluetoothGatt == null) {return;}mBluetoothGatt.close();mBluetoothGatt = null;}

大概的的流程就是这样,关于特性更多操作还需要自己去看文档。这里只是写个demo,让大家了解一下流程,下面给出全部带代码,结合步骤来看:

全部代码

package cn.small_qi.bluetoothtest.ble;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.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;import cn.small_qi.bluetoothtest.R;
import cn.small_qi.bluetoothtest.classic.MsgAdapter;
import cn.small_qi.bluetoothtest.classic.RvAdapter;
import cn.small_qi.bluetoothtest.gattserver.GattServerActivity;@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class BleMainActivity extends AppCompatActivity implements View.OnClickListener {private static final String TAG = "BleMainActivity";public static final int REQUEST_BT_ENABLE_CODE = 200;private BluetoothAdapter mBluetoothAdapter;//蓝牙适配器private MyLeScanCallback1 mLeScanCallback1;//搜索回调1private MyLeScanCallback2 mLeScanCallback2;//搜索回调2private BluetoothGatt mBluetoothGatt;//GATT客户端private BluetoothLeScanner mBleScanner;//BLE扫描器private boolean mScanning;//是否正在搜索private RecyclerView devicesView, msgsView;private RvAdapter mDeviceAdapter;private MsgAdapter mMessageAdapter;private List<BluetoothGattService> mServiceList;//服务列表private SimpleDateFormat mDateFormat;private boolean isCallback1=true;private static Handler mHandler = new Handler() {@Overridepublic void dispatchMessage(Message msg) {super.dispatchMessage(msg);}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_ble_main);mDateFormat = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());initUI();initList();}private void outputLog(String msg){if (mMessageAdapter!=null){mMessageAdapter.addMessage(mDateFormat.format(System.currentTimeMillis())+"  "+msg);}}private void initUI() {findViewById(R.id.open).setOnClickListener(this);findViewById(R.id.close).setOnClickListener(this);findViewById(R.id.start).setOnClickListener(this);findViewById(R.id.stop).setOnClickListener(this);findViewById(R.id.callback_type).setOnClickListener(this);mLeScanCallback1 = new MyLeScanCallback1();mLeScanCallback2 = new MyLeScanCallback2();}private void initList() {mDeviceAdapter = new RvAdapter(this);mMessageAdapter = new MsgAdapter(this);devicesView = (RecyclerView) findViewById(R.id.devices);devicesView.setLayoutManager(new LinearLayoutManager(this));devicesView.setAdapter(mDeviceAdapter);msgsView = (RecyclerView) findViewById(R.id.msglist);msgsView.setLayoutManager(new LinearLayoutManager(this));msgsView.setAdapter(mMessageAdapter);mDeviceAdapter.setOnItemClickListener(new RvAdapter.OnItemClickListener() {@Overridepublic void onClick(BluetoothDevice device) {connectGatt(device);}});}private void connectGatt(BluetoothDevice device) {outputLog("正在连接到:"+device.getName());//新建一个链接mBluetoothGatt = device.connectGatt(BleMainActivity.this, false, mBluetoothGattCallback);//mBluetoothGatt.connect();//如果是断开重连//mBluetoothGatt.disconnect();//断开当前连接}private void openBT() {if (mBluetoothAdapter == null) {/**这里和传统的蓝牙有什么区别呢?*/final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);mBluetoothAdapter = bluetoothManager.getAdapter();//mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//如果调用scanBleDevices2(),请加上这句。 Call requires API level 21if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {mBleScanner = mBluetoothAdapter.getBluetoothLeScanner();}}//1.设备不支持蓝牙,结束应用if (mBluetoothAdapter == null) {finish();return;}//2.判断蓝牙是否打开if (!mBluetoothAdapter.enable()) {//没打开请求打开Intent btEnable = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(btEnable, REQUEST_BT_ENABLE_CODE);outputLog("正在打开蓝牙");}}private void scanBleDevices1(boolean enable) {if (enable) {outputLog("正在搜索设备");outputLog("当前搜索回调:callback 1");// Stops scanning after a pre-defined scan period.mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mScanning = false;mBluetoothAdapter.stopLeScan(mLeScanCallback1);outputLog("停止搜索");}}, 10000);//设置10秒超时mScanning = true;mBluetoothAdapter.startLeScan(mLeScanCallback1);} else {outputLog("停止搜索");mScanning = false;mBluetoothAdapter.stopLeScan(mLeScanCallback1);}}private void scanBleDevices2(boolean enable) {if (mBleScanner == null) {outputLog("搜索器初始化失败");return;}if (enable) {outputLog("当前搜索回调:callback 2");outputLog("正在搜索设备");// Stops scanning after a pre-defined scan period.mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mScanning = false;mBleScanner.stopScan(mLeScanCallback2);outputLog("停止搜索");}}, 10000);//设置10秒超时mScanning = true;mBleScanner.startScan(mLeScanCallback2);} else {mScanning = false;mBleScanner.stopScan(mLeScanCallback2);outputLog("停止搜索");}}/*** 找到自己想要获取的特性,并设置更改通知* 例如,服务里面提供了心率检测和血压监测,我现在只想获取心率*/private void setCharacteristicNotification() {if (mBluetoothAdapter == null || mBluetoothGatt == null) {Log.w(TAG, "BluetoothAdapter not initialized");return;}//找到对应服务BluetoothGattCharacteristic characteristic = null;for (BluetoothGattService s : mServiceList) {if (s.getUuid().equals(GattServerActivity.TIME_SERVICE)) {//找到对应服务的特性List<BluetoothGattCharacteristic> cList = s.getCharacteristics();for (BluetoothGattCharacteristic c : cList) {if (c.getUuid().equals(GattServerActivity.CURRENT_TIME)) {//找出需要通知改变的特性characteristic = c;}}}}if (characteristic == null) {return;//服务中不包含我们需要获取的特性}//启动通知:BLE应用程序通常在设备上的特定特性发生变化时要求收到通知//一旦为特性启用通知,如果远程设备上的特性发生变化,则触发回调onCharacteristicChanged()mBluetoothGatt.setCharacteristicNotification(characteristic, true);outputLog("开启特性变化通知成功");// This is specific to Heart Rate Measurement.//这里要对应我服务端所使用的UUID,详情请查看上一篇博客//更改特性描述if (GattServerActivity.CURRENT_TIME.equals(characteristic.getUuid())) {BluetoothGattDescriptor descriptor = characteristic.getDescriptor(GattServerActivity.CLIENT_CONFIG);descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);mBluetoothGatt.writeDescriptor(descriptor);}//然后读一下这个特性mBluetoothGatt.readCharacteristic(characteristic);//会触发回调,这里就到回调中处理}//写特性,其他操作类似,直接使用mBluetoothGatt调用对应的方法即可private boolean writeCharacteristic(BluetoothGattCharacteristic characteristic){if (mBluetoothGatt!=null){return mBluetoothGatt.writeCharacteristic(characteristic);}return false;}/*** 关闭蓝牙或者退出应用别忘了关闭客户端哦*/private void close() {if (mBluetoothGatt == null) {return;}mBluetoothGatt.close();mBluetoothGatt = null;}@Overrideprotected void onDestroy() {close();super.onDestroy();}/*** GATT操作回调*/private BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {@Overridepublic void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {super.onPhyUpdate(gatt, txPhy, rxPhy, status);}@Overridepublic void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {super.onPhyRead(gatt, txPhy, rxPhy, status);}@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {super.onConnectionStateChange(gatt, status, newState);if (newState == BluetoothProfile.STATE_CONNECTED) {//成功连接outputLog("连接蓝牙服务成功");mBluetoothGatt.discoverServices();//搜索服务器中的包含服务outputLog("搜索外围服务");} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {//断开连接outputLog("断开蓝牙服务");}}@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {super.onServicesDiscovered(gatt, status);if (status == BluetoothGatt.GATT_SUCCESS) {outputLog("成功搜索到服务");//找到服务//获取服务列表mServiceList = gatt.getServices();//设置特性更改通知setCharacteristicNotification();} else {outputLog("服务搜索失败");Log.w(TAG, "onServicesDiscovered received: " + status);}}@Overridepublic void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicRead(gatt, characteristic, status);if (status == BluetoothGatt.GATT_SUCCESS) {//读取特性//characteristic.getValue();outputLog("读取到特性:"+new String(characteristic.getValue()));}}@Overridepublic void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicWrite(gatt, characteristic, status);if (status == BluetoothGatt.GATT_SUCCESS) {//反写成功}}@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {super.onCharacteristicChanged(gatt, characteristic);//服务端特性改变 读取新数据outputLog("接收到特性变化:"+new String(characteristic.getValue()));gatt.readCharacteristic(characteristic);}@Overridepublic void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {super.onDescriptorRead(gatt, descriptor, status);if (status == BluetoothGatt.GATT_SUCCESS) {//参考特性的处理}}@Overridepublic void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {super.onDescriptorWrite(gatt, descriptor, status);if (status == BluetoothGatt.GATT_SUCCESS) {//反写成功}}@Overridepublic void onReliableWriteCompleted(BluetoothGatt gatt, int status) {super.onReliableWriteCompleted(gatt, status);}@Overridepublic void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {super.onReadRemoteRssi(gatt, rssi, status);}@Overridepublic void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {super.onMtuChanged(gatt, mtu, status);}};@Overridepublic void onClick(View view) {switch (view.getId()) {case R.id.open:openBT();break;case R.id.close:if (mBluetoothAdapter!=null){mBluetoothAdapter.disable();close();}break;case R.id.start:if (isCallback1) {scanBleDevices1(true);}else {scanBleDevices2(true);}break;case R.id.stop:if (isCallback1) {scanBleDevices1(false);}else {scanBleDevices2(false);}break;case R.id.callback_type:isCallback1=!isCallback1;break;}}/*** 搜索回调1* 不同于传统蓝牙:这里不需要使用广播接收结果,而是使用回调*/class MyLeScanCallback1 implements BluetoothAdapter.LeScanCallback {@Overridepublic void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {mDeviceAdapter.addDevice(bluetoothDevice);}}/*** 搜索回调2* 不同于传统蓝牙:这里不需要使用广播接收结果,而是使用回调* <p>* callbackType:CALLBACK_TYPE_ALL_MATCHES, CALLBACK_TYPE_FIRST_MATCH or CALLBACK_TYPE_MATCH_LOST*/class MyLeScanCallback2 extends ScanCallback {@Overridepublic void onScanResult(int callbackType, ScanResult result) {super.onScanResult(callbackType, result);mDeviceAdapter.addDevice(result.getDevice());}@Overridepublic void onScanFailed(int errorCode) {super.onScanFailed(errorCode);}@Overridepublic void onBatchScanResults(List<ScanResult> results) {super.onBatchScanResults(results);//批量结果}}
}

测试结果截图:

非常可惜~现在暂时不能提供截图!我手上只有一台支持ble的手机做不了,带来了妹纸已经淘汰华为荣耀7,欢天喜地拿来想做测试。结果发现在荣耀7上开启服务时,其他都正常,但是无法广播出来。无法获取广播实例,坑爹的,估计是华为把这个api给修改或者去掉了。

那好,它当不了服务器,能当客户端吧~

我用我的一加3T,成功开启服务,并且广播出来。在荣耀7上开启客户端程序,什么打开蓝牙,扫描,连接都成功。已经提示“连接服务成功”了。可是迟迟不出现“搜索外围服务”,证明mBluetoothGatt.discoverServices();又没执行!我真是不知道说什么好了~不知道是不是手机问题。等我去借(坑)别人的手机来试试,再给大家截图。如果是代码有问题呢~我会直接在这篇博客中说明~

好了~关于蓝牙也就到这里结束,感谢您来看安卓小白小汪汪的博客~小白毕竟小白,有问题在所难免,希望各位老司机在评论给出意见或者建议~

----------------------------------------------------------------------------最近更新--------------------------------------------------------------------------------------------------

之前说华为荣耀7  mBluetoothGatt.discoverServices();  不执行是我错了!但是荣耀7没有广播的功能是真的!它当不了服务器!换别的品牌都可以。先来看看之前的代码:

@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {super.onConnectionStateChange(gatt, status, newState);if (newState == BluetoothProfile.STATE_CONNECTED) {//成功连接outputLog("连接蓝牙服务成功");mBluetoothGatt.discoverServices();//搜索服务器中的包含服务outputLog("搜索外围服务");} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {//断开连接outputLog("断开蓝牙服务");}}@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {super.onServicesDiscovered(gatt, status);if (status == BluetoothGatt.GATT_SUCCESS) {outputLog("成功搜索到服务");//找到服务//获取服务列表mServiceList = gatt.getServices();//设置特性更改通知setCharacteristicNotification();} else {outputLog("服务搜索失败");Log.w(TAG, "onServicesDiscovered received: " + status);}}

我的本意是连接成功之后,立马搜索外围设备提供的服务。实际证明,连接成功之后要等待几秒才能发起服务搜索,猜测可能是什么东西没初始化完成,所以发起搜索失败。1):要么你就连接成功之后,等待3秒在执行搜索操作。1):要么点击连接之后,过三秒在发起一次连接(原理一样)。3):不用全局变量发起搜索,使用回调参数中的gatt发起搜索。代码修改如下:

@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {super.onConnectionStateChange(gatt, status, newState);if (newState == BluetoothProfile.STATE_CONNECTED) {//成功连接outputLog("连接蓝牙服务成功");//修改方式1 连接成功之后3秒再发起一次连接。mBluetoothGatt.discoverServices();//搜索服务器中的包含服务//修改方式2mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mBluetoothGatt.discoverServices();}},3000);//修改方式3gatt.discoverServices();outputLog("搜索外围服务");} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {//断开连接outputLog("断开蓝牙服务");}}@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {super.onServicesDiscovered(gatt, status);if (status == BluetoothGatt.GATT_SUCCESS) {outputLog("成功搜索到服务");//找到服务//获取服务列表mServiceList = gatt.getServices();//设置特性更改通知setCharacteristicNotification();} else {outputLog("服务搜索失败");Log.w(TAG, "onServicesDiscovered received: " + status);}}

然后就可以正常进行通讯了。可能有人发现,搜索时,列表中会有很多相同的设备出现!BLE搜索是接收广播,外围设备一直推送,所以就出现几个一样的。所以大家在日常开发都要判断排除相同的设备。

下面附上运行截图 服务器:坚果pro(偷妹子的手机来测试了)  客户端:一加3T /荣耀7:

服务器:

客户端:

Android BLE低功耗蓝牙开发(下) BLE客户端(中央设备)与GATT服务的通讯相关推荐

  1. Android BLE低功耗蓝牙开发

    啦啦啦在上一个项目中有用到BLE低功耗蓝牙开发,当时baidu google了很多资料,但大多数都是千篇一律,英文文档我这种渣渣又看不懂...总之刚开始查的很痛苦.所以要把自己的踩坑之路写下来记录下, ...

  2. Android 8.0 BLE 低功耗蓝牙开发记录

    Android 8.0 BLE 低功耗蓝牙开发记录(1-3)--------------(权限申请篇未完待续) 目的:开源博客,希望大家一起修改博客错误地方,共同完善并会鸣谢提供意见的朋友.为大家提供 ...

  3. ble 低功耗蓝牙开发学习 嵌入式交流学习

    ble 低功耗蓝牙开发学习 嵌入式交流学习 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 这篇文章教你学会低功耗蓝牙开发,从0到深入,适合自学的学生.初级工程师 前言 随着疫情爆发 ...

  4. c# 低功耗蓝牙_Android ble低功耗蓝牙开发-客户端

    什么是BLE(低功耗蓝牙) BLE(Bluetooth Low Energy,低功耗蓝牙)是对传统蓝牙BR/EDR技术的补充. 尽管BLE和传统蓝牙都称之为蓝牙标准,且共享射频,但是,BLE是一个完全 ...

  5. Android ble covana,Android BLE低功耗蓝牙开发

    最近做了一个智能硬件开发(针灸仪)的项目,有一部分涉及到低功耗蓝牙的开发,就是通过蓝牙和设备进行数据的交互,比如控制改设备的LED的开关,设备的开关机,设置设备的时间和温度等,下面就项目中遇到的坑一一 ...

  6. android低耗能蓝牙开发,Android BLE低功耗蓝牙开发

    最近做了一个智能硬件开发(针灸仪)的项目,有一部分涉及到低功耗蓝牙的开发,就是通过蓝牙和设备进行数据的交互,比如控制改设备的LED的开关,设备的开关机,设置设备的时间和温度等,下面就项目中遇到的坑一一 ...

  7. BLE低功耗蓝牙开发学习,从零到深教程文档总结(持续更新2022/6/14更新)

    写在前面: 写教程原因: 说说自己写这次的ble教程的由来吧.以往公司总有很多是做单片机的或者应届生毕业,他们对ble不是很连接,公司一般都会安排别人来做一点培训啊,或者老员工带.巧了,之前帮别的培训 ...

  8. 08_微信小程序-BLE低功耗蓝牙开发-设备搜索

    遇到的一些问题 BLE相关的好多函数都是异步的,但是BLE的操作又必须按照顺序流程来,否则就会出现问题,所以这里得嵌套大量的回调函数. 普通回调函数中this拿不到data数据的问题 原因:小程序在回 ...

  9. 20_微信小程序-BLE低功耗蓝牙开发-发布小程序

    所有功能测试OK了,就剩下最后一步了,那就是把开发好的微信小程序发布出去. 1. 填写小程序信息,登录小程序管理平台,在设置->填写信息,里面填写小程序相关信息(后面我直接把小程序名称改为&qu ...

最新文章

  1. djaogo知识点 python_python Django知识点总结
  2. 一气发了3个patch
  3. java初始化实例化_Java 类初始化和实例化以及多态理解
  4. Go - interface
  5. 计算机中有关数及编码的知识,计算机中有关数及编码的知识
  6. win10 4k分屏 eclipse等工具打开后按钮图标大小问题解决方案
  7. .net Api 接口调用 增删改查
  8. 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借助TopSelf、服务类】)【转载】...
  9. 【白皮书分享】2021智慧城市白皮书:城市建设运营数字化转型.pdf(附下载链接)...
  10. 托管项目到github
  11. 『深度应用』一文搞懂深度学习人脸识别模型开发流程
  12. 轻松实现Word在线编辑
  13. WinFR 界面版 - 免费好用的数据恢复软件,误删文件轻松找回
  14. 今日更新京东皮卡丘1.7修复链接和预约抢购下单商品,手机端茅台抢购软同步上架(可配备群控使用)
  15. 初入职场着装宝典(BOY)
  16. WIN7 旗舰版 万能KEY
  17. maven-assembly-plugin
  18. 小学三年级计算机导学案,小学三年级下册科学导学案
  19. BarManage --- 菜单
  20. OSChina 周三乱弹 ——学哪种编程语言能保住一头秀发?

热门文章

  1. 上海万应云数科——助力地方政府和产业园建立数字资产运营管理平台
  2. Mina Logging Filter日志过滤器知识介绍
  3. Firefox 火狐浏览器强制刷新(忽略缓存)的快捷键
  4. 浪潮,惠普服务器BMC远程安装系统
  5. 策略模式+注解,代替if-else
  6. x299服务器芯片,【华硕X299评测】处理器和主板的选择-中关村在线
  7. 项目管理精华--给非职业项目经理人的项目管理书
  8. (php毕业设计)基于php校园食堂点餐管理系统源码
  9. SSM酒店管理系统项目Day22
  10. 我们是如何在研发过程中控制质量的?产品质量正变得越来越重要