当前位置:   article > 正文

Android蓝牙开发—经典蓝牙详细开发流程_android 经典蓝牙 发现 连接

android 经典蓝牙 发现 连接

原文出处

标题:Android蓝牙开发—经典蓝牙详细开发流程

作者:Errol_King

原文链接:Android蓝牙开发—经典蓝牙详细开发流程_蓝牙开发流程-CSDN博客

开发流程

  • 开启蓝牙
  • 扫描蓝牙,并将这些设备加入到devices列表
  • 配对蓝牙,将次设备加入到已配对设备列表
  • 连接蓝牙
  • 通信

一些解释

设备列表:是指附近的可见蓝牙设备,但是还没匹配
匹配设备列表:是指已经完成匹配的列表

设备类型分为:【未匹配状态】、【匹配状态】、【配对状态】

未匹配,就是指从来没有匹配过的设备
匹配状态,就是指设备间已经相互完成了身份验证,处于待配对(待建立Socket)状态

以前蓝牙配对的时候,手机会弹出一个对话框,提示输入pin码,其实也就是提前约定的一个配对码,到后来,手机与与手机之间的连接就不需要配对码了(实际上是程序内部完成了配对的过程)

当然,手机与一些蓝牙硬件(例如单片机+蓝牙模块的组合)配对时,还是需要输入pin码(不过也能通过程序自动完成验证)

配对的过程,其实就是Socket通信的一个过程,两个蓝牙设备仅仅匹配是还不能够传递数据的,只有当二者建立了Socket通道之后,才能进行数据的传递

权限

  1. <!-- 使用蓝牙的权限 -->
  2. <uses-permission android:name="android.permission.BLUETOOTH" />
  3. <!-- 扫描蓝牙设备或者操作蓝牙设置 -->
  4. <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  5. <!--模糊定位权限,仅作用于6.0+-->
  6. <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  7. <!--精准定位权限,仅作用于6.0+-->
  8. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

后边的模糊定位和精准定位是必须的,没有这两个权限,注册BroadcastReceiver 监听扫描结果时,不会成功的,不走扫描开始之类的方法

核心API

Android的蓝牙开发有涉及到不同的蓝牙种类,例如低功耗蓝牙(BluetoothGatt)、蓝牙健康(BlueToothHealth)等,这里介绍的依然是常规的蓝牙开发API

BlueToothAdapter

这个类代表着本地的蓝牙适配器,让你可以从事各种与蓝牙相关的操作,例如开始和停止设备的查找;查询已匹配的设备并以集合的形式返回;通过已知的设备地址,实例化一个BlueToothDevice;创建一个BluetoothServerSocket来监听来自其他设备的链接请求等等,总之要想使用本机的蓝牙,这个类是极其重要的

getDefaultAdapter():获取BluetoothAdapter对象

  1. //获取本地蓝牙适配器
  2. bluetoothAdapter=BluetoothAdapter.getDefaultAdapter();

判断设备是否支持蓝牙

  1. /**
  2. * 设备是否支持蓝牙 true为支持
  3. * @return
  4. */
  5. public boolean isSupportBlue(){
  6. return bluetoothAdapter!= null;
  7. }

判断蓝牙是否开启

  1. //获取BluetoothAdapter对象
  2. mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  3. //判断设备是否支持蓝牙,如果mBluetoothAdapter为空则不支持,否则支持
  4. if (mBluetoothAdapter == null) {
  5. Toast.makeText(this, "这台设备不支持蓝牙", Toast.LENGTH_SHORT).show();
  6. } else {
  7. // If BT is not on, request that it be enabled.
  8. // setupChat() will then be called during onActivityResult
  9. //判断蓝牙是否开启,如果蓝牙没有打开则打开蓝牙
  10. if (!mBluetoothAdapter.isEnabled()) {
  11. //请求用户开启
  12. Intent enableIntent = new Intent(
  13. BluetoothAdapter.ACTION_REQUEST_ENABLE);
  14. startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
  15. } else {
  16. getDeviceList();
  17. }
  18. }

判断请求后是否开启

  1. @Override
  2. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  3. super.onActivityResult(requestCode, resultCode, data);
  4. if (requestCode == REQUEST_ENABLE_BT) {
  5. if (resultCode == Activity.RESULT_OK) {
  6. // bluetooth is opened
  7. //可以获取列表操作等
  8. } else {
  9. // bluetooth is not open
  10. Toast.makeText(this, "蓝牙没有开启", Toast.LENGTH_SHORT).show();
  11. }
  12. }
  13. }

getBondedDevices()

获取已经匹配的设备列表,并以set集合的方式返回

  1. /*
  2. *获取已经配对的设备
  3. */
  4. private void setPairingDevice() {
  5. Set<BluetoothDevice> devices = bluetoothAdapter.getBondedDevices();
  6. if (devices.size() > 0) { //存在已配对过的设备
  7. //利用for循环读取每一个设备的信息
  8. for (Iterator<BluetoothDevice> it = devices.iterator(); it.hasNext(); ) {
  9. BluetoothDevice btd = it.next();
  10. ```````
  11. }
  12. }else{
  13. //不存在已经配对的蓝牙设备
  14. }
  15. }

isDiscovering()

判断当前是否正在查找设备,是返回true

cancelDiscovery()

取消查找设备

startDiscovery()

这里需要注意一下,由于搜素是一个耗时操作,所以这个方法应该在线程中去调用,同时需要配合着广播去使用

开始查找设备,一般的调用代码如下:

  1. private void doDiscovery() {
  2. new Thread(new Runnable() {
  3. @Override
  4. public void run() {
  5. if(bluetoothAdapter.isDiscovering()){
  6. bluetoothAdapter.cancelDiscovery();
  7. }
  8. bluetoothAdapter.startDiscovery();
  9. }
  10. }).start();
  11. }

这里再注意一个细节,那就是如果当前的adapter正在查找,那么必须停止当前查找,然后再重新查找,这是因为查找操作占用很多的系统资源,我们需要避免重复的查找

getAddress() | getName() | getState()

getAddress()获取本地蓝牙地址

getName()获取本地蓝牙名称

getState()获取本地蓝牙适配器当前状态

BluetoothDevice

这个类代表一个远程设备,它能够让我们与一个其他相关设备创建连接,或者查询与此设备相关的信息,例如名称(name)、地址(address)、连接状态(boud state)

getAddress() | getName() | getState()

getAddress()获取Address设备的蓝牙地址
getName()获取Address设备的地蓝牙名称
getState()获取Address设备的蓝牙适配器当前状态,是匹配状态,不是连接状态

功能实现

扫描蓝牙

开始扫描

这部分代码之前已经写过

  1. if (bluetoothAdapter.isDiscovering()) {
  2. bluetoothAdapter.cancelDiscovery();
  3. }
  4. // Request discover from BluetoothAdapter
  5. bluetoothAdapter.startDiscovery();

通过广播的方式接收扫描结果

注册广播

  1. //搜索开始的过滤器
  2. IntentFilter filter1 = new IntentFilter(android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_STARTED);
  3. //搜索结束的过滤器
  4. IntentFilter filter2 = new IntentFilter(android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
  5. //寻找到设备的过滤器
  6. IntentFilter filter3 = new IntentFilter(BluetoothDevice.ACTION_FOUND);
  7. //绑定状态改变
  8. IntentFilter filer4 = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
  9. //配对请求
  10. IntentFilter filter5 = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST)
  11. registerReceiver(mFindBlueToothReceiver ,filter1);
  12. registerReceiver(mFindBlueToothReceiver ,filter2);
  13. registerReceiver(mFindBlueToothReceiver ,filter3);
  14. registerReceiver(mFindBlueToothReceiver ,filter4);
  15. registerReceiver(mFindBlueToothReceiver ,filter5);

接收广播

  1. //广播接收器,当远程蓝牙设备被发现时,回调函数onReceiver()会被执行
  2. private final BroadcastReceiver mFindBlueToothReceiver = new BroadcastReceiver() {
  3. @Override
  4. public void onReceive(Context context, Intent intent) {
  5. String action = intent.getAction();
  6. BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  7. switch (action){
  8. case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
  9. Log.d(TAG, "开始扫描...");
  10. callBack.onScanStarted();
  11. break;
  12. case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
  13. Log.d(TAG, "结束扫描...");
  14. callBack.onScanFinished();
  15. break;
  16. case BluetoothDevice.ACTION_FOUND:
  17. Log.d(TAG, "发现设备...");
  18. callBack.onScanning(device);
  19. break;
  20. case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
  21. Log.d(TAG, "设备绑定状态改变...");
  22. callBack.onStateChanged(device);
  23. break;
  24. }
  25. }
  26. };

配对蓝牙

配对指定设备

其中布尔值returnValue为true,表示配对成功,配对成功后,可能会输入PIN码
当输入完成正确PIN码后,才会配对完成。就可以通过广播的方式接收配对结果

  1. /**
  2. * 配对蓝牙设备
  3. */
  4. private void pinTargetDevice(int position) {
  5. //在配对之前,停止搜索
  6. cancelDiscovery();
  7. //获取要匹配的BluetoothDevice对象,后边的deviceList是你本地存的所有对象
  8. BluetoothDevice device = deviceList.get(position);
  9. if (device.getBondState() != BluetoothDevice.BOND_BONDED) {//没配对才配对
  10. try {
  11. Log.d(TAG, "开始配对...");
  12. Method createBondMethod = BluetoothDevice.class.getMethod("createBond");
  13. Boolean returnValue = (Boolean) createBondMethod.invoke(device);
  14. if (returnValue){
  15. Log.d(TAG, "配对成功...");
  16. showToast("配对成功");
  17. }
  18. } catch (NoSuchMethodException e) {
  19. e.printStackTrace();
  20. } catch (IllegalAccessException e) {
  21. e.printStackTrace();
  22. } catch (InvocationTargetException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }

取消配对

  1. /**
  2. * 取消配对(取消配对成功与失败通过广播返回 也就是配对失败)
  3. * @param device
  4. */
  5. public void cancelPinBule(BluetoothDevice device){
  6. Log.d(TAG, "attemp to cancel bond:" + device.getName());
  7. try {
  8. Method removeBondMethod = device.getClass().getMethod("removeBond");
  9. Boolean returnValue = (Boolean) removeBondMethod.invoke(device);
  10. returnValue.booleanValue();
  11. } catch (Exception e) {
  12. // TODO Auto-generated catch block
  13. e.printStackTrace();
  14. Log.e(TAG, "attemp to cancel bond fail!");
  15. }
  16. }

经测试,不管用,貌似只有在系统设置里才能取消配对?所以我的做法是跳转了蓝牙设置

  startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));

通过广播的方式接收配对结果

  1. /**
  2. * 广播接收器
  3. * 当远程蓝牙设备被发现时,回调函数onReceiver()会被执行
  4. */
  5. private final BroadcastReceiver mFindBlueToothReceiver = new BroadcastReceiver() {
  6. @Override
  7. public void onReceive(Context context, Intent intent) {
  8. String action = intent.getAction();
  9. // Get the BluetoothDevice object from the Intent
  10. BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  11. switch (action){
  12. ......
  13. case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
  14. Log.d(TAG, "设备绑定状态改变...");
  15. switch (device.getBondState()) {
  16. case BluetoothDevice.BOND_BONDING:
  17. Log.w(TAG, "正在配对......");
  18. break;
  19. case BluetoothDevice.BOND_BONDED:
  20. Log.w( TAG, "配对完成");
  21. break;
  22. case BluetoothDevice.BOND_NONE:
  23. Log.w(TAG, "取消配对");
  24. default:
  25. break;
  26. }
  27. break;
  28. }
  29. }
  30. };

蓝牙连接

蓝牙配对和连接是两个不同的东西
经典蓝牙连接相当于socket连接,是个非常耗时的操作,所以应该放到子线程中去完成

蓝牙连接线程

  1. /**
  2. * 蓝牙连接线程
  3. */
  4. public class ConnectBlueTask extends AsyncTask<BluetoothDevice, Integer, BluetoothSocket> {
  5. private BluetoothDevice bluetoothDevice;
  6. private ConnectBlueCallBack callBack;
  7. public ConnectBlueTask(ConnectBlueCallBack callBack){
  8. this.callBack = callBack;
  9. }
  10. @Override
  11. protected BluetoothSocket doInBackground(BluetoothDevice... bluetoothDevices) {
  12. bluetoothDevice = bluetoothDevices[0];
  13. BluetoothSocket socket = null;
  14. try{
  15. Log.d(TAG,"开始连接socket,uuid:00001101-0000-1000-8000-00805F9B34FB");
  16. socket = bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
  17. if (socket != null && !socket.isConnected()){
  18. socket.connect();
  19. }
  20. }catch (IOException e){
  21. Log.e(TAG,"socket连接失败");
  22. try {
  23. socket.close();
  24. } catch (IOException e1) {
  25. e1.printStackTrace();
  26. Log.e(TAG,"socket关闭失败");
  27. }
  28. }
  29. return socket;
  30. }
  31. @Override
  32. protected void onPreExecute() {
  33. Log.d(TAG,"开始连接");
  34. if (callBack != null) callBack.onStartConnect();
  35. }
  36. @Override
  37. protected void onPostExecute(BluetoothSocket bluetoothSocket) {
  38. if (bluetoothSocket != null && bluetoothSocket.isConnected()){
  39. Log.d(TAG,"连接成功");
  40. if (callBack != null) callBack.onConnectSuccess(bluetoothDevice, bluetoothSocket);
  41. }else {
  42. Log.d(TAG,"连接失败");
  43. if (callBack != null) callBack.onConnectFail(bluetoothDevice, "连接失败");
  44. }
  45. }
  46. }

启动连接线程

  1. /**
  2. * 连接 (在配对之后调用)
  3. * @param device
  4. */
  5. public void connect(BluetoothDevice device, ConnectBlueCallBack callBack){
  6. //连接之前把扫描关闭
  7. if (mBluetoothAdapter.isDiscovering()){
  8. mBluetoothAdapter.cancelDiscovery();
  9. }
  10. new ConnectBlueTask(callBack).execute(device);
  11. }

其中ConnectBlueCallBack是个接口,用于连接之后回调

  1. public interface ConnectBlueCallBack{
  2. void onStartConnect();
  3. void onConnectSuccess(BluetoothDevice device,BluetoothSocket bluetoothSocket);
  4. void onConnectFail(BluetoothDevice device,String string);
  5. }

所以在真正调用以上conn方法时是这样写的,传入一个连接的device,并且new一个刚才定义的接口

  1. connect(device, new ConnectBlueCallBack() {
  2. @Override
  3. public void onStartConnect() {
  4. Log.w( TAG, "开始连接");
  5. }
  6. @Override
  7. public void onConnectSuccess(BluetoothDevice device, BluetoothSocket bluetoothSocket) {
  8. Log.w( TAG, "连接成功");
  9. }
  10. @Override
  11. public void onConnectFail(BluetoothDevice device, String string) {
  12. Log.w( TAG, "连接失败");
  13. }
  14. });

值得注意的是,如果你不知道UUID也不用担心,一些常见的蓝牙服务协议已经有约定的 UUID。比如我们连接热敏打印机是基于 SPP 串口通信协议,其对应的 UUID 是 “00001101-0000-1000-8000-00805F9B34FB”,所以上边的一串数字被我写死了

判断是否连接成功

  1. /**
  2. * 蓝牙是否连接
  3. * @return
  4. */
  5. public boolean isConnectBlue(){
  6. return mBluetoothSocket != null && mBluetoothSocket.isConnected();
  7. }

断开连接

  1. /**
  2. * 断开连接
  3. * @return
  4. */
  5. public boolean cancelConnect(){
  6. if (mBluetoothSocket != null && mBluetoothSocket.isConnected()){
  7. try {
  8. mBluetoothSocket.close();
  9. } catch (IOException e) {
  10. e.printStackTrace();
  11. return false;
  12. }
  13. }
  14. mBluetoothSocket = null;
  15. return true;
  16. }

参考文章

android 蓝牙锁应用开发实例(三)蓝牙相关功能实现【第一部分】
Android蓝牙开发—经典蓝牙详细开发流程

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/956256
推荐阅读
相关标签
  

闽ICP备14008679号