Android蓝牙通信之搜索蓝牙设备

发布时间 - 2026-01-11 03:28:24    点击率:

一:注意事项

      1:android6.0使用蓝牙时,需要开启gps定位权限,不然无法搜索其它蓝牙设备。

二:权限

      1:权限配置

<!--允许程序连接到已配对的蓝牙设备-->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 允许程序发现和配对蓝牙设备 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
 <!--android 6.0 涉及到的权限-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 在SDCard中创建与删除文件的权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据的权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    2:动态权限代码

    由于需要用到存储卡,定位等,android6.0以上需要代码动态设置。

   a)获取定位设置

if (Build.VERSION.SDK_INT >= 23) {
  boolean isLocat = isLocationOpen(getApplicationContext());
  Toast.makeText(mContext, "isLo:" + isLocat, Toast.LENGTH_LONG).show();
   //开启位置服务,支持获取ble蓝牙扫描结果
  if (!isLocat) {
  Intent enableLocate = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
  startActivityForResult(enableLocate, 1);
  }
}
 /**
  * 判断位置信息是否开启
  *
  * @param context
  * @return
  */
 private static boolean isLocationOpen(final Context context) {
  LocationManager manager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
  //gps定位
  boolean isGpsProvider = manager.isProviderEnabled(LocationManager.GPS_PROVIDER);
  //网络定位
  boolean isNetWorkProvider = manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
  return isGpsProvider || isNetWorkProvider;
 }

b)存储卡权限设置

if (Build.VERSION.SDK_INT >= 23) {
   int write = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
   int read = checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
   //动态请求读写sd卡权限
   if (write != PackageManager.PERMISSION_GRANTED || read != PackageManager.PERMISSION_GRANTED) {
    requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, SD_CARD);
   }
}

然后通过onRequestPermissionsResult()方法获取动态权限的结果:

@Override
 public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
  switch (requestCode){
   case SD_CARD:
    if(grantResults.length>0&&grantResults[0] == PackageManager.PERMISSION_GRANTED){
     //允许访问
    }else{
     Toast.makeText(mContext,"您拒绝了程序访问存储卡",Toast.LENGTH_LONG).show();
    }
    break;
   case COARES_LOCATION:
    break;
  }
 }

 三:蓝牙搜索

android.bluetooth.BluetoothAdapter 是蓝牙开发用得比较多,并且比较重要的一个类,可以设备蓝牙名称,打开,关闭,搜索等常规操作。

  1 蓝牙打开,以及搜索

     蓝牙打开和关闭信息使用BluetoothAdapter.ACTION_STATE_CHANGED去接收广播

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter.setName("blueTestPhone"); 
//判断蓝牙是否打开
boolean originalBluetooth = (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled());
if (originalBluetooth) {
 mBluetoothAdapter.startDiscovery();
} else if (originalBluetooth == false) {
 mBluetoothAdapter.enable();
}

蓝牙打开后,我们可以获取设备的蓝牙信息

StringBuilder sb = new StringBuilder();
 //获取本机蓝牙名称
 String name = mBluetoothAdapter.getName();
//获取本机蓝牙地址
 String address = mBluetoothAdapter.getAddress();

搜索完成后,通过BluetoothDevice.ACTION_FOUND广播去接收结果,广播代码如下(注意:可能出现设备搜索不到的情况,设备需要开启允许周围设备搜索,或者通过程序来控制允许搜索的时间范围)

 /*确保蓝牙被发现,在荣耀8手机上,设置了还是默认的2分钟,所以以下几句代码程序中没有,*/
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
//设置可见状态的持续时间为300秒,但是最多是300秒
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivityForResult(discoverableIntent, REQUEST_DISCOVERABLE_BLUETOOTH);
private void initSearchBroadcast() {
  IntentFilter intentFilter = new IntentFilter();
  //发现设备
  intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
  //设备配对状态改变
  intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
  //蓝牙设备状态改变
  intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
  //开始扫描
  intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
  //结束扫描
  intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
  //其它设备请求配对
  intentFilter.addAction(ACTION_PAIRING_REQUEST);
  //intentFilter.addAction(BluetoothAdapter.CONNECTION_STATE_CHANGED);
  registerReceiver(bluetoothReceiver, intentFilter);
 }
private BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
   String action = intent.getAction();
   Logger.e(TAG + "mBluetoothReceiver action =" + action);
   try {
    if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {//开始扫描
     setProgressBarIndeterminateVisibility(true);
     log1.setText("正在扫描设备,请稍候...");
    } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {//结束扫描
     Logger.e(TAG + "设备搜索完毕");
     setProgressBarIndeterminateVisibility(false);
     log1.setText("扫描完成");
     bondAdapter.notifyDataSetChanged();
     unbondAdapter.notifyDataSetChanged();
     scanStatus = false;
    } else if (BluetoothDevice.ACTION_FOUND.equals(action)) {//发现设备
     findDevice(intent);
    } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {//蓝牙配对状态的广播
     BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
     Logger.e(TAG + device.getName() + "蓝牙配对广播:" + device.getBondState());
     switch (device.getBondState()) {
      case BluetoothDevice.BOND_BONDING:
       Logger.e(TAG + device.getName() + "蓝牙配对广播 正在配对......");
       break;
      case BluetoothDevice.BOND_BONDED:
       Logger.e(TAG + device.getName() + "蓝牙配对广播 完成配对,本机自动配对");
       bondDevices.add(device);
       unbondDevices.remove(device);
       bondAdapter.notifyDataSetChanged();
       unbondAdapter.notifyDataSetChanged(); 
       break;
      case BluetoothDevice.BOND_NONE:
       Logger.e(TAG + device.getName() + "蓝牙配对广播 取消配对");
       unbondDevices.add(device);
       bondDevices.remove(device);
       unbondAdapter.notifyDataSetChanged();
       bondAdapter.notifyDataSetChanged();
      default:
       break;
     }
    } else if (action.equals(ACTION_PAIRING_REQUEST)) {//其它设备蓝牙配对请求
     BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
     int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); //当前的配对的状态
     try {
      String path = Environment.getExternalStorageDirectory() + "/blueTest/";
      String deviceName = btDevice.getName();
      Logger.e(TAG + "蓝牙 匹配信息:" + deviceName + "," + btDevice.getAddress() + ",state:" + state);
      //1.确认配对,高版本无效,蓝牙配对不是zuk的问题,而是安卓6.0的bug,凡是遇到蓝牙适配问题的,请同时打开蓝牙和定位,再去配对,基本90%都没有问题了。
      Object object = ClsUtils.setPairingConfirmation(btDevice.getClass(), btDevice, true);
      //2.终止有序广播,如果没有将广播终止,则会出现一个一闪而过的配对框。
      abortBroadcast();
      //3.调用setPin方法进行配对...
      boolean ret = ClsUtils.setPin(btDevice.getClass(), btDevice, PWD);
     } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      Toast.makeText(mContenxt, "error:" + btDevice + "," + state, Toast.LENGTH_LONG).show();
     }
    } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {//蓝牙开关状态
     // BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
     int statue = mBluetoothAdapter.getState();
     switch (statue) {
      case BluetoothAdapter.STATE_OFF:
       Logger.e("蓝牙状态:,蓝牙关闭");
       ClsUtils.closeDiscoverableTimeout(mBluetoothAdapter);
       break;
      case BluetoothAdapter.STATE_ON:
       Logger.e("蓝牙状态:,蓝牙打开");
       ClsUtils.setDiscoverableTimeout(1000 * 60, mBluetoothAdapter);
       scanBluetooth();
       break;
      case BluetoothAdapter.STATE_TURNING_OFF:
       Logger.e("蓝牙状态:,蓝牙正在关闭");
       mBluetoothAdapter.cancelDiscovery();
       break;
      case BluetoothAdapter.STATE_TURNING_ON:
       Logger.e("蓝牙状态:,蓝牙正在打开");
       break;
     }
    }
   } catch (Exception e) {
    e.printStackTrace();
   }
  }
 };
//发现设备的代码如下
 private void findDevice(Intent intent) throws Exception{
 //获取到设备对象
 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
 String str = device.getName() + "|" + device.getAddress();
 Logger.e("扫描到设备:" + str);
 if (device.getBondState() == BluetoothDevice.BOND_BONDED) {//判断当前设备地址下的device是否已经配对
  if (!bondDevices.contains(device)) {
   bondDevices.add(device);
  }
 } else {
  if (!unbondDevices.contains(device)) {
   unbondDevices.add(device);
  }
  if (device.getName().equals(TEST_DEVICE_NAME)) {
   boolean bondStatus = ClsUtils.createBond(device.getClass(), device);
   Logger.i(TAG + " bondStatus:" + bondStatus);
  }
 }
 Log.e("error", "搜索完毕,准备刷新!");
 bondAdapter.notifyDataSetChanged();
 unbondAdapter.notifyDataSetChanged();
}

四:蓝牙配对

   正常情况下,蓝牙匹配需要弹出一个匹配确认框,如下图,但我想实现的是,匹配其中一方,不能手动点击配对,因为发起蓝牙连接的设备是android设备,是不能触摸的,所以就要通过程序来解决这个问题,特别声明:(测试的android设备,版本为5.x,并且已经root,没有root的设备,或者不是android5.x不清楚能否实现自动匹配,因为我只有这个测试设备)。

1 当我们搜索到目标手机的蓝牙后,android设备主动发起连接请求,代码如下

 if (device.getName().equals(TEST_DEVICE_NAME)) {
    boolean bondStatus = ClsUtils.createBond(device.getClass(), device);
    Logger.i(TAG + " bondStatus:" + bondStatus);
 }
//发起蓝牙匹配请求 
public boolean createBond(Class btClass, BluetoothDevice btDevice)
   throws Exception {
  Method createBondMethod = btClass.getMethod("createBond");
  Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
  return returnValue.booleanValue();
}

2 当被匹配方点击配对后,系统会通过BluetoothDevice.ACTION_BOND_STATE_CHANGED广播告诉android设备,此时android设备就可以自动确认,通过这个流程来完成整个蓝牙的配对,具体代码如下

 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
     int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); //当前的配对的状态
     try {
      String path = Environment.getExternalStorageDirectory() + "/blueTest/";
      String deviceName = btDevice.getName();
      Logger.e(TAG + "蓝牙 匹配信息:" + deviceName + "," + btDevice.getAddress() + ",state:" + state); 
      if(deviceName.equals(TEST_DEVICE_NAME)){//TEST_DEVICE_NAME 为被匹配蓝牙设备的名称,自己手动定义
       Object object = ClsUtils.setPairingConfirmation(btDevice.getClass(), btDevice, true);
       abortBroadcast();
       boolean ret = ClsUtils.setPin(btDevice.getClass(), btDevice, PWD);
      }
     } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      Toast.makeText(mContenxt, "error:" + btDevice + "," + state, Toast.LENGTH_LONG).show();
     }
//确认配对
public Object setPairingConfirmation(Class<?> btClass, BluetoothDevice device, boolean isConfirm) throws Exception {
  Method setPairingConfirmation = btClass.getDeclaredMethod("setPairingConfirmation", boolean.class);
  Object object = setPairingConfirmation.invoke(device, isConfirm);
   return object;
 }
//配对需要调用的方法
public boolean setPin(Class<? extends BluetoothDevice> btClass, BluetoothDevice btDevice,
         String str) throws Exception {
  try {
   Method removeBondMethod = btClass.getDeclaredMethod("setPin",
     new Class[]
       {byte[].class});
   Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice,
     new Object[]
       {str.getBytes()});
   Log.e("returnValue", "" + returnValue);
  } catch (SecurityException e) {
   // throw new RuntimeException(e.getMessage());
   e.printStackTrace();
  } catch (IllegalArgumentException e) {
   // throw new RuntimeException(e.getMessage());
   e.printStackTrace();
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return true;
 }     

      到目前为止,蓝牙权限,以及动态权限,蓝牙的打开,关闭,搜索,以及自动配对(特别声明:(自动配对的android设备,版本为5.x,并且已经root,没有root的设备,或者不是android5.x不清楚能否实现自动匹配,因为我只有这个测试设备)。)代码至此结束。

demo代码下载:github

总结

以上所述是小编给大家介绍的Android蓝牙通信之搜索蓝牙设备,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!


# Android蓝牙通信  # 搜索蓝牙设备  # Android 扫描附近的蓝牙设备并连接蓝牙音响的示例  # Android获取蓝牙设备列表的方法  # 本机  # 存储卡  # 不清楚  # 小编  # 的是  # 因为我  # 最多  # 在此  # 我们可以  # 给大家  # 如果没有  # 弹出  # 而过  # 几句  # 时间为  # 再去  # 当我们  # 涉及到  # 比较多  # 请稍候 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 手机网站制作平台,手机靓号代理商怎么制作属于自己的手机靓号网站?  Python制作简易注册登录系统  如何在建站主机中优化服务器配置?  Laravel Eloquent关联是什么_Laravel模型一对一与一对多关系精讲  Laravel怎么配置不同环境的数据库_Laravel本地测试与生产环境动态切换【方法】  如何在万网利用已有域名快速建站?  什么是JavaScript解构赋值_解构赋值有哪些实用技巧  如何用AI一键生成爆款短视频文案?小红书AI文案写作指令【教程】  Laravel怎么导出Excel文件_Laravel Excel插件使用教程  高配服务器限时抢购:企业级配置与回收服务一站式优惠方案  制作无缝贴图网站有哪些,3dmax无缝贴图怎么调?  Laravel Debugbar怎么安装_Laravel调试工具栏配置指南  Laravel Facade的原理是什么_深入理解Laravel门面及其工作机制  高防服务器如何保障网站安全无虞?  Laravel如何使用Blade模板引擎?(完整语法和示例)  详解一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)  如何快速查询网站的真实建站时间?  phpredis提高消息队列的实时性方法(推荐)  Python结构化数据采集_字段抽取解析【教程】  Laravel中Service Container是做什么的_Laravel服务容器与依赖注入核心概念解析  Laravel路由怎么定义_Laravel核心路由系统完全入门指南  历史网站制作软件,华为如何找回被删除的网站?  如何基于云服务器快速搭建个人网站?  微信小程序 scroll-view组件实现列表页实例代码  JavaScript实现Fly Bird小游戏  Laravel的路由模型绑定怎么用_Laravel Route Model Binding简化控制器逻辑  如何在IIS中新建站点并配置端口与IP地址?  电商网站制作价格怎么算,网上拍卖流程以及规则?  制作旅游网站html,怎样注册旅游网站?  googleplay官方入口在哪里_Google Play官方商店快速入口指南  如何快速完成中国万网建站详细流程?  Microsoft Edge如何解决网页加载问题 Edge浏览器加载问题修复  Laravel如何实现数据导出到PDF_Laravel使用snappy生成网页快照PDF【方案】  油猴 教程,油猴搜脚本为什么会网页无法显示?  Laravel怎么写单元测试_PHPUnit在Laravel项目中的基础测试入门  javascript中的数组方法有哪些_如何利用数组方法简化数据处理  HTML透明颜色代码怎么让图片透明_给img元素加透明色的技巧【方法】  Laravel怎么返回JSON格式数据_Laravel API资源Response响应格式化【技巧】  Laravel怎么进行数据库回滚_Laravel Migration数据库版本控制与回滚操作  5种Android数据存储方式汇总  Laravel集合Collection怎么用_Laravel集合常用函数详解  JavaScript模板引擎Template.js使用详解  香港网站服务器数量如何影响SEO优化效果?  简历没回改:利用AI润色让你的文字更专业  INTERNET浏览器怎样恢复关闭标签页_INTERNET浏览器标签恢复快捷键与方法【指南】  Laravel怎么做数据加密_Laravel内置Crypt门面的加密与解密功能  七夕网站制作视频,七夕大促活动怎么报名?  Laravel如何实现登录错误次数限制_Laravel自带LoginThrottles限流配置【方法】  如何在万网自助建站平台快速创建网站?  bing浏览器学术搜索入口_bing学术文献检索地址