【android 蓝牙开发——蓝牙耳机】

【android 蓝牙开发——传统蓝牙】

【android 蓝牙开发——BLE(低功耗)蓝牙 2021-10-09更新】

总结一下蓝牙开发的基本使用以及蓝牙耳机的断开和链接。

所以需权限:

  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- Android12 的蓝牙权限 如果您的应用与已配对的蓝牙设备通信或者获取当前手机蓝牙是否打开 --><uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- Android12 的蓝牙权限 如果您的应用查找蓝牙设备(如蓝牙低功耗 (BLE) 外围设备) --><uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <!-- Android12 的蓝牙权限 如果您的应用使当前设备可被其他蓝牙设备检测到 --><uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />

注意这里需要位置权限。

开启权限:

 ActivityResultLauncher<Intent> bluetoothOpenLaunch;private void startBluetooth() {bluetoothOpenLaunch = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {@Overridepublic void onActivityResult(ActivityResult result) {System.out.println("MainActivity.onActivityResult" + result.getResultCode());if (Activity.RESULT_OK == result.getResultCode()) {getPermission();}}});Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {ArrayList<String> arrayListOf = new ArrayList<>();arrayListOf.add(Manifest.permission.BLUETOOTH_CONNECT);PermissionUtils.permission(String.valueOf(arrayListOf)).callback(new PermissionUtils.SimpleCallback() {@Overridepublic void onGranted() {System.out.println("MainActivity.onGranted");bluetoothOpenLaunch.launch(intent);}@Overridepublic void onDenied() {System.out.println("MainActivity.onDenied");ToastUtils.showLong("请开启蓝牙权限");}}).request();} else {bluetoothOpenLaunch.launch(intent);}}private void getPermission() {ArrayList<String> arrayListOf = new ArrayList<>();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {arrayListOf.add(Manifest.permission.BLUETOOTH_SCAN);arrayListOf.add(Manifest.permission.BLUETOOTH_CONNECT);arrayListOf.add(Manifest.permission.BLUETOOTH_ADVERTISE);}arrayListOf.add(Manifest.permission.BLUETOOTH);arrayListOf.add(Manifest.permission.BLUETOOTH_ADMIN);arrayListOf.add(Manifest.permission.ACCESS_FINE_LOCATION);arrayListOf.add(Manifest.permission.ACCESS_COARSE_LOCATION);PermissionUtils.permission(String.valueOf(arrayListOf)).callback(new PermissionUtils.SimpleCallback() {@Overridepublic void onGranted() {ToastUtils.showLong("蓝牙相关权限已成功授权");BluetoothManager blueManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);//mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()mBluetoothAdapter = blueManager.getAdapter();}@Overridepublic void onDenied() {ToastUtils.showLong("请开启蓝牙权限");}}).request();}

监听蓝牙连接配对等相关广播,建议直接采用以下方式:(也有其他方法,搜索低功耗蓝牙的方式 startLeScan )

 //注册BoradcasrReceiverprivate void registerReceiver() {IntentFilter discoveryFilter = new IntentFilter();discoveryFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);discoveryFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);discoveryFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);discoveryFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);discoveryFilter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);discoveryFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);discoveryFilter.addAction(BluetoothDevice.ACTION_FOUND);registerReceiver(discoveryReceiver, discoveryFilter);}//蓝牙搜索广播的接收器@SuppressLint("MissingPermission")private BroadcastReceiver discoveryReceiver = new BroadcastReceiver() {String pin = "0000";  //此处为你要连接的蓝牙设备的初始密钥,一般为 1234 或 0000@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  //获取设备,发现远程蓝牙设备if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {Log.i(TAG, "onReceive: 开始搜索");} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {Log.i(TAG, "onReceive:  搜索结束");} else if (BluetoothDevice.ACTION_FOUND.equals(action)) {// //获取扫描到的设备String name = bluetoothDevice.getName();String address = bluetoothDevice.getAddress();Log.i(TAG, "onReceive: name=" + name + " address=" + address);/*if (bluetoothDevice.getBondState() == BluetoothDevice.BOND_BONDED) {Log.i(TAG, "已配对 onReceive: name=" + name + " address=" + address);} else {}*///已经配对boolean isExist = false;for (DeviceBean device : bluetoothDevices) {if (device.getAddress().equals(address)) {isExist = true;}}if (!isExist) {bluetoothDevices.add(new DeviceBean(name, address));mAdapter.notifyDataSetChanged();}} else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {//Log.i(TAG, "onReceive:  绑定状态改变");int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);Log.i(TAG, "onReceive:  绑定状态改变 state=" + state);if (state == 12) {Log.i(TAG, "onReceive:  绑定成功 ");ToastUtils.showLong("绑定成功");String name = bluetoothDevice.getName();String address = bluetoothDevice.getAddress();boolean isExist = false;for (DeviceBean device : pairedDevices) {if (device.getAddress().equals(address)) {isExist = true;}}if (!isExist) {/*for (DeviceBean device : pairedDevices) {device.setStatus(false);}*/DeviceBean deviceBean = new DeviceBean(name, address);deviceBean.setStatus(true);pairedDevices.add(0, deviceBean);mDevicePairedAdapter.notifyDataSetChanged();} else {for (DeviceBean device : pairedDevices) {if (device.getAddress().equals(address)) {device.setStatus(true);mDevicePairedAdapter.notifyDataSetChanged();break;}}}}} else if (BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {//Log.i(TAG, "onReceive:  连接状态改变");int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, BluetoothAdapter.ERROR);String name = bluetoothDevice.getName();String address = bluetoothDevice.getAddress();Log.i(TAG, "onReceive:  连接状态改变 state=" + state);switch (state) {case 0://断开成功for (DeviceBean device : pairedDevices) {if (device.getAddress().equals(address)) {device.setStatus(false);mDevicePairedAdapter.notifyDataSetChanged();break;}}break;//case 1: break;case 2://连接成功Log.i(TAG, "onReceive:  连接成功 address=" + address);ToastUtils.showLong("连接成功");for (DeviceBean device : pairedDevices) {if (device.getAddress().equals(address)) {device.setStatus(true);mDevicePairedAdapter.notifyDataSetChanged();break;}}break;default:break;}} else if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)) {Log.i(TAG, "onReceive:  配对");try {// bluetoothDevice.setPairingConfirmation(true);// abortBroadcast();// bluetoothDevice.setPin(pin.getBytes());//1:/*  ClsUtils.setPairingConfirmation(bluetoothDevice.getClass(),bluetoothDevice,true);//2:如果没有将广播终止,则会出现一个一闪而过的配对框。abortBroadcast();//3.调用setPin方法进行配对..boolean ret = ClsUtils.setPin(bluetoothDevice.getClass(), bluetoothDevice, pin);System.out.println("ConnectingDevicesActivity.onReceive ret="+ret);*/} catch (Exception e) {throw new RuntimeException(e);}} else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {Log.i(TAG, "onReceive:   蓝牙开关状态改变");int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);switch (state) {case BluetoothAdapter.STATE_OFF:break;case BluetoothAdapter.STATE_ON:break;}} else {Log.i(TAG, "onReceive:   else action=" + action);}}};

我们也可以获取已配对蓝牙列表:

private void initData() {Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();if (bondedDevices != null && bondedDevices.size() > 0) {for (BluetoothDevice bondedDevice : bondedDevices) {System.out.println("ConnectingDevicesActivity.initData name==" + bondedDevice.getName());DeviceBean deviceBean = new DeviceBean(bondedDevice.getName(), bondedDevice.getAddress());pairedDevices.add(deviceBean);}mDevicePairedAdapter.notifyDataSetChanged();}}

蓝牙耳机断开和连接的具体方法:

  public boolean isDisconnected = false;/*** 断开蓝牙设备连接** @param bluetoothDevice BluetoothDevice*/public void disconnect(BluetoothDevice bluetoothDevice) {currentBluetoothDevice = bluetoothDevice;//获取A2DP代理对象mBluetoothAdapter.getProfileProxy(this, disconnectProfileServiceListener, BluetoothProfile.HEADSET);//获取HEADSET代理对象mBluetoothAdapter.getProfileProxy(this, disconnectProfileServiceListener, BluetoothProfile.A2DP);}private final BluetoothProfile.ServiceListener disconnectProfileServiceListener = new BluetoothProfile.ServiceListener() {@Overridepublic void onServiceDisconnected(int profile) {System.out.println("ConnectingDevicesActivity.onServiceDisconnected profile="+profile);}@Overridepublic void onServiceConnected(int profile, BluetoothProfile proxy) {try {if (profile == BluetoothProfile.HEADSET) {//使用HEADSET的协议断开蓝牙设备(使用了反射技术调用断开的方法)BluetoothHeadset bluetoothHeadset = (BluetoothHeadset) proxy;boolean isDisConnect = false;try {Method connect = null;if (isDisconnected) {connect = bluetoothHeadset.getClass().getDeclaredMethod("disconnect", BluetoothDevice.class); //disconnect connect} else {connect = bluetoothHeadset.getClass().getDeclaredMethod("connect", BluetoothDevice.class); //disconnect connect}connect.setAccessible(true);isDisConnect = (boolean) connect.invoke(bluetoothHeadset, currentBluetoothDevice);if (isDisconnected) {Log.d(TAG, "isDisConnect:" + (isDisConnect ? "断开通话成功" : "断开通话失败") + currentBluetoothDevice.getName());} else {Log.d(TAG, "isDisConnect:" + (isDisConnect ? "链接通话成功" : "链接通话失败") + currentBluetoothDevice.getName());}} catch (Exception e) {e.printStackTrace();}}if (profile == BluetoothProfile.A2DP) {//使用A2DP的协议断开蓝牙设备(使用了反射技术调用断开的方法)BluetoothA2dp bluetoothA2dp = (BluetoothA2dp) proxy;boolean isDisConnect = false;try {Method connect = null;if (isDisconnected) {connect = bluetoothA2dp.getClass().getDeclaredMethod("disconnect", BluetoothDevice.class);} else {connect = bluetoothA2dp.getClass().getDeclaredMethod("connect", BluetoothDevice.class);}connect.setAccessible(true);isDisConnect = (boolean) connect.invoke(bluetoothA2dp, currentBluetoothDevice);if (isDisconnected) {Log.d(TAG, "isDisConnect:" + (isDisConnect ? "断开音频成功" : "断开音频失败") + currentBluetoothDevice.getName());} else {Log.d(TAG, "isDisConnect:" + (isDisConnect ? "链接音频成功" : "链接音频失败") + currentBluetoothDevice.getName());}} catch (Exception e) {e.printStackTrace();}if (isDisConnect) {for (DeviceBean device : pairedDevices) {if (device.getAddress().equals(currentBluetoothDevice.getAddress())) {device.setStatus(!isDisconnected);mDevicePairedAdapter.notifyDataSetChanged();break;}}}}} catch (Exception e) {e.printStackTrace();}}};

源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/78035.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务·架构组件之网关

微服务架构组件之网关 引言 微服务架构已成为构建大型和复杂应用程序的流行范式之一。在微服务架构中&#xff0c;通常一个系统会被拆分为多个微服务&#xff0c;如果 客户端多次请求不同的微服务&#xff0c;会增加客户端代码和配置的复杂性&#xff0c;维护成本比较高。每…

用冒泡排序完成库函数qsort的作用

Hello&#xff0c;今天分享的是我们用冒泡函数实现qsort&#xff0c;也就是快排&#xff0c;之前我们也讲过库函数qsort的使用方法&#xff0c;今天我们尝试用冒泡函数实现一下&#xff0c;当然我们也见过qsort&#xff0c;后面也会继续完善的。这几天我是破防大学生&#xff0…

Github上1.1KFork的C++笔记

编程语言(C/C) 原文链接&#xff0c;如果觉得本文对你有所帮助&#xff0c;欢迎去原地址点个Star⭐。侵删 https://github.com/linw7/Skill-Tre 目录 Chapter 1Chapter 2Chapter 3Chapter 4编程基础面向对象基础标准模板库编译及调试 内容 Chapter1&#xff1a;编程基础 C/…

Debian11安装PostgreSQL+PostGIS+pgRouting ,链接Navicat

船新版本&#xff0c;遵循官网教程 1 准备一个Debian11系统2 从官网安装Postgres2.1 安装Postgres2.2 修改Postgres密码2.3 配置Postgres远程访问 3 安装Postgis、pgRouting4 链接Navicat 1 准备一个Debian11系统 2 从官网安装Postgres 2.1 安装Postgres 1 进入Postgre的官网…

第一类曲线积分与二重积分在极坐标系下表示的区别

1.第一类曲线积分与二重积分在极坐标系下表示的区别 区别主要来源于一是曲线积分的积分区域为边界&#xff0c;而二重积分的积分区域为内部边界&#xff0c;二是极点位置选取的不同&#xff0c;二者共同造成在积分区域在极坐标下表示的不同&#xff0c;即 ρ \rho ρ是常量还是…

uboot 顶层Makefile-make xxx_deconfig过程说明三

一. uboot 的 make xxx_deconfig配置 本文接上一篇文章的内容。地址如下&#xff1a;uboot 顶层Makefile-make xxx_deconfig过程说明二_凌肖战的博客-CSDN博客 本文继续来学习 uboot 源码在执行 make xxx_deconfig 这个配置过程中&#xff0c;顶层 Makefile有关的执行思路。 …

spring boot+redis整合基础入门

文章目录 前言准备依赖项配置文件redis模板类注入设置序列化方式 实施基础字符串操作、超时设置Hash操作hash的使用场景以及优缺点 列表操作列表操作的应用场景以及优缺点 Set的基础操作Set类型的业务场景以及优缺点Demo地址 总结 前言 最近项目中有用到redis进行一些数据的缓…

导数公式及求导法则

目录 基本初等函数的导数公式 求导法则 有理运算法则 复合函数求导法 隐函数求导法 反函数求导法 参数方程求导法 对数求导法 基本初等函数的导数公式 基本初等函数的导数公式包括&#xff1a; C0(x^n)nx^(n-1)(a^x)a^x*lna(e^x)e^x(loga(x))1/(xlna)(lnx)1/x(sinx)cos…

Python---数据容器分类及通用操作

数据容器可以从以下视角进行简单的分类: 是否支持下标索引&#xff1a; 支持:列表、元组、字符串 - 序列类型不支持:集合、字典 - 非序列类型 是否支持重复元素: 支持:列表、元组、字符串 - 序列类型不支持:集合、字典 - 非序列类型 是否可以修改&#xff1a; 支持:列表、…

阿里云服务器配置怎么选择?几核几G?带宽系统盘怎么选?

阿里云服务器配置选择_CPU内存/带宽/存储配置_小白指南&#xff0c;阿里云服务器配置选择方法包括云服务器类型、CPU内存、操作系统、公网带宽、系统盘存储、网络带宽选择、安全配置、监控等&#xff0c;阿小云分享阿里云服务器配置选择方法&#xff0c;选择适合自己的云服务器…

C++系列-空指针访问成员函数及野指针

C系列-空指针访问成员函数 空指针空指针访问成员函数 野指针 空指针 在变量声明的时候&#xff0c;如果没有确切的地址可以赋值&#xff0c;为指针变量赋一个 NULL 值是一个良好的编程习惯。NULL指针是没有指向任何内容的指针。赋为 NULL 值的指针被称为空指针。 在大多数的操…

用selenium webdriver获取网站cookie后,实现免登录上网站

以csdn为例&#xff0c;代码分为两部分。 一、csdn_get_cookies.py为半手动登录网站后获取cookies 二、csdn_use_cookies.py为使用获取到的cookies免登录上网站 #获取登录cookiesfrom selenium import webdriver import jsoncsdn_driver webdriver.Chrome() url "htt…

ChatGPT OpenAI 完成Excel组合函数Vlookup+match多条件查找

ChatGPT OpenAI 现在已经助力职场办公。 我们现在有这样一个Excel需求: 根据姓名与科目查找对应的分数。可以使用Vlookup+match组合函数一起来实现 。 我们将公式复制到Excel中来进行验证。 ChatGPT生成的Excel函数公式可以直接进行使用。 更多实战内容。

Android:使用命令行发现keytool不是内部命令解决办法

一、前言&#xff1a;最近在搞引入高德地图的SDK&#xff0c;发现需要给app签名打包。记录一下。 二、当我在命令行中输入keytool的时候说keytool不是内部命令 解决方案&#xff1a; 找到系统属性--------高级----------点击环境变量 双击点开 找到java\jre1.8.0_202\bin新建…

CSS3技巧36:backdrop-filter 背景滤镜

CSS3 有 filter 滤镜属性&#xff0c;能给内容&#xff0c;尤其是图片&#xff0c;添加各种滤镜效果。 filter 滤镜详见博文&#xff1a;CSS3中强大的filter(滤镜)属性_css3滤镜_stones4zd的博客-CSDN博客 后续&#xff0c;CSS3 又新增了 backdrop-filter 背景滤镜。 backdr…

SAP SD之定义装运点OVL2

什么是装运点&#xff1f; 装运点是一个独立的组织实体&#xff0c;其中进行货物的发行和交付处理。 可以为每个订单商品确定一个装运点。 确定装运点取决于以下三个因素&#xff1a; 客户主记录中的运输条款和条件&#xff08;运输屏幕&#xff09;。 例如&#xff0c;公司与…

BS EN 14323:2021 室内用三聚氰胺纸贴面木基板材检测

三聚氰胺贴面纸木基板材是指未经处理的三聚氰胺纸通过粘接或者热压的方式直接覆于木质基材表面的装饰板材&#xff0c;通常基材为刨花板&#xff0c;密度板等&#xff0c;主要用于室内。 BS EN 14323:2021 室内用三聚氰胺纸贴面木基板材测试项目 测试项目 测试方法 外观 EN…

180B参数的Falcon登顶Hugging Face,vs chatGPT 最好开源大模型使用体验

文章目录 使用地址使用体验test1:简单喜好类问题test2:知识性问题test3:开放性问题test4:中文支持test5:问题时效性test6:学术问题使用地址 https://huggingface.co/spaces/tiiuae/falcon-180b-demo 使用体验 相比Falcon-7b,Falcon-180b拥有1800亿的参数量

程序员面试逻辑题

红白帽子推理 答案&#xff1a; 这个题有点像数学归纳法&#xff0c;就是假设有 A A A和 B B B两个人是黑色的帽子&#xff0c;这样的话第一次开灯&#xff0c; A A A看到 B B B是黑色的&#xff0c;其他人都是白色的&#xff0c;那么 A A A会觉得 B B B是那个黑色的&#xff0…

第28章_瑞萨MCU零基础入门系列教程之基于面向对象的工程结构

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…