Android BLE低功耗蓝牙开发(下) BLE客户端(中央设备)与GATT服务的通讯
来源:互联网 发布:在淘宝上怎样分期付款 编辑:程序博客网 时间:2024/04/30 01:30
之前的文章简单实现了使用传统蓝牙进行通讯的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() { @Override public 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 { @Override public 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() { @Override public 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 { @Override public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) { mDeviceAdapter.addDevice(bluetoothDevice); } }
传统蓝牙连接别的设备:
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 21 if (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;//搜索回调1 private MyLeScanCallback2 mLeScanCallback2;//搜索回调2 private 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 21 if (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() { @Override public 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() { @Override public 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 { @Override public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) { mDeviceAdapter.addDevice(bluetoothDevice); } }
API>=21
class MyLeScanCallback2 extends ScanCallback { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); mDeviceAdapter.addDevice(result.getDevice()); } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); } @Override public 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() { @Override public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { super.onPhyUpdate(gatt, txPhy, rxPhy, status); } @Override public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { super.onPhyRead(gatt, txPhy, rxPhy, status); } @Override public 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("断开蓝牙服务"); } } @Override public 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); } } @Override public 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())); } } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { //反写成功 } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); //服务端特性改变 读取新数据 outputLog("接收到特性变化:"+new String(characteristic.getValue())); gatt.readCharacteristic(characteristic); } @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorRead(gatt, descriptor, status); if (status == BluetoothGatt.GATT_SUCCESS) { //参考特性的处理 } } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); if (status == BluetoothGatt.GATT_SUCCESS) { //反写成功 } } @Override public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { super.onReliableWriteCompleted(gatt, status); } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { super.onReadRemoteRssi(gatt, rssi, status); } @Override public 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;//搜索回调1 private MyLeScanCallback2 mLeScanCallback2;//搜索回调2 private 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() { @Override public void dispatchMessage(Message msg) { super.dispatchMessage(msg); } }; @Override protected 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() { @Override public 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 21 if (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() { @Override public 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() { @Override public 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; } @Override protected void onDestroy() { close(); super.onDestroy(); } /** * GATT操作回调 */ private BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() { @Override public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { super.onPhyUpdate(gatt, txPhy, rxPhy, status); } @Override public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { super.onPhyRead(gatt, txPhy, rxPhy, status); } @Override public 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("断开蓝牙服务"); } } @Override public 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); } } @Override public 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())); } } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { //反写成功 } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); //服务端特性改变 读取新数据 outputLog("接收到特性变化:"+new String(characteristic.getValue())); gatt.readCharacteristic(characteristic); } @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorRead(gatt, descriptor, status); if (status == BluetoothGatt.GATT_SUCCESS) { //参考特性的处理 } } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); if (status == BluetoothGatt.GATT_SUCCESS) { //反写成功 } } @Override public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { super.onReliableWriteCompleted(gatt, status); } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { super.onReadRemoteRssi(gatt, rssi, status); } @Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { super.onMtuChanged(gatt, mtu, status); } }; @Override public 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 { @Override public 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 { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); mDeviceAdapter.addDevice(result.getDevice()); } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); } @Override public void onBatchScanResults(List<ScanResult> results) { super.onBatchScanResults(results); //批量结果 } }}
测试结果截图:
非常可惜~现在暂时不能提供截图!我手上只有一台支持ble的手机做不了,带来了妹纸已经淘汰华为荣耀7,欢天喜地拿来想做测试。结果发现在荣耀7上开启服务时,其他都正常,但是无法广播出来。无法获取广播实例,坑爹的,估计是华为把这个api给修改或者去掉了。
那好,它当不了服务器,能当客户端吧~
我用我的一加3T,成功开启服务,并且广播出来。在荣耀7上开启客户端程序,什么打开蓝牙,扫描,连接都成功。已经提示“连接服务成功”了。可是迟迟不出现“搜索外围服务”,证明mBluetoothGatt.discoverServices();又没执行!我真是不知道说什么好了~不知道是不是手机问题。等我去借(坑)别人的手机来试试,再给大家截图。如果是代码有问题呢~我会直接在这篇博客中说明~
好了~关于蓝牙也就到这里结束,感谢您来看安卓小白小汪汪的博客~小白毕竟小白,有问题在所难免,希望各位老司机在评论给出意见或者建议~
----------------------------------------------------------------------------最近更新--------------------------------------------------------------------------------------------------
之前说华为荣耀7 mBluetoothGatt.discoverServices(); 不执行是我错了!但是荣耀7没有广播的功能是真的!它当不了服务器!换别的品牌都可以。先来看看之前的代码:
@Override public 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("断开蓝牙服务"); } } @Override public 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发起搜索。代码修改如下:
@Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); if (newState == BluetoothProfile.STATE_CONNECTED) { //成功连接 outputLog("连接蓝牙服务成功"); //修改方式1 连接成功之后3秒再发起一次连接。 mBluetoothGatt.discoverServices();//搜索服务器中的包含服务 //修改方式2 mHandler.postDelayed(new Runnable() { @Override public void run() { mBluetoothGatt.discoverServices(); } },3000); //修改方式3 gatt.discoverServices(); outputLog("搜索外围服务"); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { //断开连接 outputLog("断开蓝牙服务"); } } @Override public 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服务的通讯
- Android BLE低功耗蓝牙开发(上)关于GATT服务器的理论与搭建
- Android 蓝牙低功耗Ble Gatt浅谈
- TI低功耗蓝牙(BLE)GATT介绍
- 【android 蓝牙开发——BLE(低功耗)蓝牙】
- 低功耗蓝牙4.0BLE编程-nrf51822开发(8)-GATT
- 低功耗蓝牙4.0BLE编程-nrf51822开发(8)-GATT
- 低功耗蓝牙4.0BLE编程-nrf51822开发(8)-GATT
- 低功耗蓝牙(BLE)
- 低功耗蓝牙(BLE)
- 低功耗蓝牙(BLE)
- 低功耗蓝牙(BLE)
- Android 低功耗蓝牙(BLE)开发(1)-- 基本概念
- Android 低功耗蓝牙(BLE)开发(2)-- BluetoothAdapter详解
- Android 低功耗蓝牙(BLE)开发(3)-- BluetoothDevice详解
- Android-低功耗蓝牙BLE(Bluetooth Low Energy)开发
- [BLE]低功耗蓝牙之GAP、GATT
- BLE低功耗蓝牙设备开发
- How to modify comment and attribute ClearCase
- String和StringBuilder以及StringBuffer的区别用法
- Acm 1087 Super Jumping! Jumping! Jumping!
- UIPageControl设置圆点之间距离
- java基础之多态、接口、抽象类
- Android BLE低功耗蓝牙开发(下) BLE客户端(中央设备)与GATT服务的通讯
- 元素是否被覆盖
- Android Studio导入项目
- ros 段错误 pluginlib
- 安卓广播监听WiFi信号强弱的变化
- 基于NMAP日志文件的暴力破解工具BruteSpray
- 2017第二届IT运营新动能大会免费为您解读解读ITOM的新动能!
- 从恩智浦系列芯片学习高频RFID
- #ifdef __OBJC__ 宏定义的作用