Android系统编程入门系列之硬件交互——通信硬件Bluetooth
通信硬件NFC的文章,虽然可以在Android系统中通过非直接接触的形式与支持NFC硬件的设备通信,但是也只能交互一些简短的标签内容,对大量的持续性数据,却并不能很好的支持。因此针对这个弊端,可以考虑使用支持Bluetooth技术的硬件。
Android系统支持传统的Bluetooth技术,其实现功能不仅可以传输数据,还可以传输并执行远程控制指令。在Android4.3 即API 18 及以后的版本中,低功耗的Bluetooth技术(简称为BLE)取自传统Bluetooth的核心功能,可以更省功耗并支持数据传输功能。在传统蓝牙技术中,应用程序所持有的蓝牙设备可以作为蓝牙服务端,开启蓝牙等待处理其他蓝牙设备的接入请求;也可以作为蓝牙客户端,开启蓝牙查找附近的蓝牙设备并请求接入。而在BLE技术中,应用程序所持有的蓝牙设备只能作为GATT客户端,连接其他类似蓝牙耳机等BLE设备。下面应用程序将会以这三种身份分别说明。
权限声明
使用Bluetooth技术,需要在清单文件中声明蓝牙相关权限。申请权限使用<uses-permission />标签,并设置其android:name属性为对应的权限名。蓝牙相关权限中主要有三种权限,申请后可分别对应执行不同功能。首先是可以执行蓝牙通信的基础权限,其值为Manifest.permission.BLUETOOTH="android.permission.BLUETOOTH";在需要通过蓝牙获取位置信息的应用程序中,还需要获取位置权限,其值为Manifest.permission.ACCESS_FINE_LOCATION="android.permission.ACCESS_FINE_LOCATION";另外如果需要使用蓝牙的远程控制功能,需要蓝牙管理员权限,其值为Manifest.permission.BLUETOOTH_ADMIN="android.permission.BLUETOOTH_ADMIN"。
使用流程
由于一个Android系统的设备上只能使用唯一的一个蓝牙硬件,因此应用程序中可以通过android.bluetooth.BluetoothAdapter蓝牙适配器操作底层的蓝牙硬件。
设置蓝牙
调用BluetoothAdapter中的静态方法getDefaultAdapter()可以获取到BluetoothAdapter蓝牙适配器对象。但是,从Android 12即API 31开始就废弃了上述方法,取而代之的是借助android.bluetooth.BluetoothManager蓝牙管理类。在能获取到Context上下文环境类对象的位置,调用其getSystemService(String name)方法,传入参数 name 值为Context.BLUETOOTH_SERVICE="bluetooth",可以获取到BluetoothManager蓝牙管理类的实例化对象,进而调用该对象的getAdapter()方法获取已经实例化的BluetoothAdapter蓝牙适配器对象。
对于不支持蓝牙硬件的设备,得到的蓝牙适配器对象为null,因此记得在应用程序中做非空判断的相关操作!
在支持蓝牙的设备上得到BluetoothAdapter对象后,还要确保设备已经开启了蓝牙功能。调用该对象的isEnabled()方法,根据返回的boolean类型结果,判断该设备是否已经正常开启了蓝牙功能;如果蓝牙处于未启用状态,还要通过调用Context上下文环境对象的startActivityForResult(Intent intent, int requestCode)方法跳转到蓝牙功能开启界面,而这里传入的参数 intent 则是标记为蓝牙界面的Intent意图对象,在该对象中设置其 action 值为BluetoothAdapter.ACTION_REQUEST_ENABLE ="android.bluetooth.adapter.action.REQUEST_ENABLE"。在跳转到蓝牙功能界面后,需要由用户手动选择开启蓝牙,并返回当前界面后,才能继续执行后续操作。
蓝牙客户端查找其他蓝牙设备
在设置蓝牙等操作之后,就是查找该设备附近范围内其他开启蓝牙功能的设备了。可以直接调用BluetoothAdapter对象的startDiscovery()方法来开启查找附近设备的功能,但是在发现设备后,系统会通过广播的形式发送已发现的设备信息给应用程序。所以还需要在某个自定义界面Activity或者服务Service中,注册BroadcastReceiver以接收该广播。
注册广播的方式可以回顾BroadcastReceiver动态注册部分内容,这里只是需要创建的IntentFilter意图过滤对象中的参数 action 值为BluetoothDevice.ACTION_FOUND="android.bluetooth.device.action.FOUND"。同时在自定义的BroadcastReceiver中处理收到的onReceive(Context context, Intent intent)方法中,对参数 intent 的getParcelableExtra(String name)方法,并设置 name 值为BluetoothDevice.EXTRA_DEVICE="android.bluetooth.device.extra.DEVICE",可以获得android.bluetooth.BluetoothDevice蓝牙硬件设备类对象,在该对象中可以获取查找到的蓝牙硬件相关信息。
在查找到设备后,要及时调用BluetoothAdapter对象的cancelDiscovery()方法关闭查找功能。否则设备的蓝牙硬件会一直占用cpu资源并消耗电量,而且在后续的建立连接及数据传输时,也会由于蓝牙发现功能的开启占用大量带宽而影响后续操作效率。
对于之前已经连接配对过的设备,就不需要通过上述查找蓝牙的方式发现设备了,可以通过调用BluetoothAdapter对象的getBondedDevices()方法,直接获取已配对过的蓝牙设备,返回BluetoothDevice对象组成的Set集合。
蓝牙服务端等待其他设备连接请求
在设置蓝牙操作后,如果想将该设备作为被发现的设备等待连接,需要启动蓝牙的可检测功能。与开启蓝牙功能界面的方法类似,调用Context上下文环境对象的startActivityForResult(Intent intent, int requestCode)方法跳转到启动蓝牙可检测性功能界面,传入的参数 intent 意图对象中,设置的 action 值为BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE="android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";另外可以通过putExtra(String name, int value)设置可检测时间,其参数 name 值为BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION="android.bluetooth.adapter.extra.DISCOVERABLE_DURATION",而参数 value 值为int类型的数值,单位为秒。
同样在跳转到蓝牙可检测性功能界面后,需要由用户手动确认开启,才能等待被其他蓝牙设备查找发现。同时不管用户确认开启或取消开启功能,都会将结果回调到Context上下文环境所代表的原启动界面Activity中的onActivityResuslt(int requestCode, int resultCode, Intent data)方法中。
蓝牙Gatt客户端查找其他BLE设备
在设置蓝牙操作后,如果只是想查找附近的BLE设备并建立连接,则需要先拿到android.bluetooth.le.BluetoothLeScannerBLE扫描类对象。通过BluetoothAdapter对象的getBluetoothLeScanner()方法,可以得到BluetoothLeScannerBLE扫描类对象。之后借助该对象的startScan(ScanCallback callback)等系列方法,开始扫描查找附近的BLE设备,在查找到符合条件的设备后,会回调android.bluetooth.le.ScanCallback类型的参数 callback 对象中的onScanResult(int callbackType, ScanResult result)方法,并将结果保存在android.bluetooth.le.ScanResult扫描结果类型的参数 result 对象中。在ScanResult对象中,可以获得连接的BLE设备BlutoothDevice对象及连接所使用的协议功能等信息。
扫描查找到BLE设备后,及时调用BluetoothLeScanner对象的stopScan(ScanCallback callback)方法,参数 callback 与上文开始查找附近BLE设备方法中的参数相同,以停止当前蓝牙扫描查找功能。
连接蓝牙
在上述查找蓝牙或蓝牙可检测功能开启后被其他设备连接后,都会得到对方的BlutetoothDevice蓝牙设备类对象。通常,应用程序所在设备查找到其他蓝牙设备后,可以主动向设备建立连接请求;而当应用程序所在设备开启蓝牙可检测功能等待其他设备查找到时,只能被动的开启连接服务,等待处理其他设备的连接请求。
蓝牙客户端发起连接请求
对于主动发起蓝牙连接请求的方式,由得到的BluetoothDevice对象调用createRfcommSocketToServiceRecord(UUID uuid)方法,可以创建远程socket连接。参数 uuid 是自定义的java.util.UUID唯一索引类对象,该值必须与后文提到的被动等待连接的蓝牙设备中的监听服务中的唯一索引值一致。该方法会返回android.bluetooth.BluetoothSocket类用以保存创建的远程蓝牙socket连接。
在得到BluetoothSocket对象后,直接调用该对象的connect()方法发起连接请求。该方法调用后会一直等待连接的建立,只有建立连接后才会正常返回并执行后续操作,否则,如果请求超时或连接失败,该方法会抛出java.io.IOException异常。
蓝牙服务端等待处理连接
对于被动等待建立连接的方式,由得到的BluetoothDevice对象调用listenUsingRfcommWithServiceRecord(String name, UUID uuid)方法,建立持续监听服务。参数 name 是为该服务定义的名字;参数 uuid 是监听的唯一索引值,其值与上文中主动发起蓝牙连接请求是创建远程socket连接所传入的参数一致。该方法返回android.bluetooth.BluetoothServerSocket蓝牙连接的服务端socket类,用以保存在等待建立连接时的socket对象。
在得到BluetoothServerSocket对象后,直接调用该对象的accept()方法监听连接请求。该方法调用后也会一致等待监听状态,只有监听到匹配的连接请求后,才会返回BluetoothSocket对象,否则,如果连接失败,该方法同样会抛出java.io.IOException异常。
对于上述两种建立连接方式执行之后,便可以通过BluetoothSocket对象在蓝牙客户端和服务端之间传输数据。该对象的getInputStream()和getOutputStream()方法可以分别获取socket连接的InputStream数据输入流对象和OutputStream数据输出流对象。可以借助InputStream数据输入流的read()系列方法,从socket连接中读取数据到应用程序中,反之借助OutputStream数据输出流的write()系列方法,将应用程序中的数据写入到socket连接中。同样地,这里的数据输入流的读取系列方法会一直处于等待读取状态,直到读到指定长度数据后才会返回结果,而数据输出流的写入系列方法,会在对方数据输入流读取慢导致写入缓存满时处于一直等待写入状态,直到指定数据完全写入才会返回结果。
在BluetoothServerSocket或BluetoothSocket对象传输数据结束后,需要分别调用他们的close()方法关闭连接,防止远程socket连接一直处于占用资源状态。
Gatt客户端连接BLE设备
对于Gatt客户端连接方式,由得到的BluetoothDevice对象调用connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)系列方法连接BLE设备的Gatt服务端,参数 context 是当前应用程序所在的上下文环境对象,参数 autoConnect 标识是否在连接该设备后自动建立Gatt服务连接,参数 callback 是建立Gatt服务连接后的回调android.bluetooth.BluetoothGattCallback抽象类。
在初次连接其他BLE设备的Gatt服务端时,会回调参数 callback 中的onServicesDiscovered(BluetoothGatt gatt, int status) 方法。而当已建立的Gatt连接状态发生改变时,会回调参数 callback 中的onConnectionStateChange(BluetoothGatt gatt, int status, int newState)方法。其他连接状态也会对应BluetoothGattCallback类中的相关方法。
在BluetoothGattCallback抽象类的回调方法中,Gatt服务连接信息是通过android.bluetooth.BluetoothGatt类的参数 gatt 来传递的。通过BluetoothGatt对象的相关方法,可以执行Gatt客户端的连接及协议控制等操作,具体可需根据应用程序需求实现。只是记得在执行完相关操作后,调用参数 gatt 的close()方法关闭连接。
————————————————————————————————————————————————————————
Android系统编程入门系列之硬件交互——通信硬件Bluetooth的更多相关文章
- Android系统编程入门系列之加载界面Activity
上回说到应用初始化加载及其生命周期,在Android系统调用Applicaiton.onCreate()之后,继续创建并加载清单文件中注册的首个界面即主Activity,也可称之为入口界面.主Acti ...
- Android系统编程入门系列之硬件交互——多媒体摄像头
多媒体系列硬件 多媒体包括图片.动画.音频.视频,这些多媒体素材的采集(输入)主要依靠摄像头和麦克风等硬件设备转化为基础数据,而他们的播放渲染(输出),则需要依靠具有相关功能的编解码软件.当然随着硬件 ...
- Android系统编程入门系列之硬件交互——传感器
到目前为止,关于应用程序与用户之间的相关内容便比较肤浅的大致介绍完毕.而在整个系统架构中,应用程序与用户之间的交互,犹如参天大树上的枝干和树叶,交互起来五彩缤纷,但使整个生态系统保持生命力的核心,在于 ...
- Android系统编程入门系列之硬件交互——通信硬件USB
在硬件交互的首篇对设备硬件的分类中,互联通信系列硬件主要用来与其他设备进行数据交互.从本文开始,将重点介绍该系列相关硬件. 互联通信系列硬件 根据硬件的可通信距离,由近及远分为USB.NFC.蓝牙.W ...
- Android系统编程入门系列之硬件交互——多媒体展示
前两篇文章通过麦克风硬件和摄像头硬件分别采集音频和视频的多媒体数据,在得到的多媒体数据通常是以编码文件的格式存储,在用户需要展示时,可通过设备的内置扩音器或蓝牙耳机等硬件播放音频,通过设备的显示屏或外 ...
- Android系统编程入门系列之硬件交互——通信硬件NFC
在上篇文章介绍了接入式USB硬件的简单使用,接下来将介绍不依赖物理连接的硬件通信了.本文的重点是近距离通信的硬件NFC. NFC硬件 应用程序中可以通过NFC硬件读取或发送指定协议的技术实现,在And ...
- Android系统编程入门系列之硬件交互——无线通信WLAN
Android系统的移动设备大多支持无线WLAN技术.利用该技术,不仅能实现互联网通信,还能实现无线定位,热点共享等远程通信功能.针对使用WLAN的不同功能,可能需要分别申请不同的权限声明,同时调用不 ...
- Android系统编程入门系列之硬件交互——通信硬件电信SIM卡
现在的SIM卡通常具备基站定位.语音通话.短信消息.网络流量这四大功能,而在移动端是无法对SIM卡使用基站定位功能的,所以这里只介绍移动端如何使用SIM卡实现语音通话.短信消息.数据流量三个功能. 语 ...
- Android系统编程入门系列之界面Activity交互响应
在上篇文章中已经了解到界面Activity的绘制完全依赖其加载的视图组件View,不仅如此,用户的每次触摸操作都可以在界面Activity内接收并响应,也可以直接传递给其中的某个视图View响应.本文 ...
随机推荐
- RabbitMQ,RocketMQ,Kafka 事务性,消息丢失和消息重复发送的处理策略
消息队列常见问题处理 分布式事务 什么是分布式事务 常见的分布式事务解决方案 基于 MQ 实现的分布式事务 本地消息表-最终一致性 MQ事务-最终一致性 RocketMQ中如何处理事务 Kafka中如 ...
- MySql各事务隔离级别及锁问题
聊事务隔离级别和锁问题之前首先得理解事务的隔离级别和共享锁及独占锁的概念: 事务的隔离级别: 脏读 不可重复读 幻读 Read uncommitted √ √ √ Read committed × ...
- uniapp云打包之后华为手机推送角标不显示(有推送没角标)
小米手机上有角标,华为和OPPO没有角标 解决方法: 华为手机添加权限(可通过反编译或者离线打包添加) < uses - permission android:name="com.hu ...
- [数学]高数部分-Part VI 重积分
Part VI 重积分 回到总目录 Part VI 重积分 二重积分的普通对称性 二重积分的轮换对称性(直角坐标系下) 二重积分直角坐标系下的积分方法 二重积分极坐标系下的积分方法 二重积分中值定理 ...
- Vue.js高效前端开发 • 【Ant Design of Vue框架进阶】
全部章节 >>>> 文章目录 一.栅格组件 1.栅格组件介绍 2.栅格组件使用 3.实践练习 二.输入组件 1.输入框组件使用 2.选择器组件使用 3.单选框组件使用 4.实践 ...
- Python_jsonPath模块的使用
jsonpath简介 如果有一个多层嵌套的复杂字典,想要根据key批量提取value,还是比较繁琐的.jsonPath模块就能解决这个痛点,接下来我们来学习一下jsonpath模块. 因为jsonpa ...
- Python_元类
什么是元类 我们知道,实例对象是由类创建的,那么类又是由什么创建的呢? 答案就是元类. 元类基本不会用到,但是就算不用,也应该去熟悉一下概念. 理解类也是对象 在大多数编程语言中,类就是一组用来描述如 ...
- spring boot + spring security +前后端分离【跨域】配置 + ajax的json传输数据
1.前言 网上各个社区的博客参差不齐 ,给初学者很大的困扰 , 我琢磨了一天一夜,到各个社区找资料,然后不断测试,遇到各种坑,一言难尽啊,要么源码只有一部分,要么直接报错... 最后实在不行,直接去看 ...
- asyncio异步编程
1. 协程 协程不是计算机提供,程序员认为创造 协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术,其实就是一个线程实现代码块相互切换执行.例如: def func1(): ...
- 【Warrior刷题笔记】剑指offer 32. 三道题,让你学会二叉树的深度广度优先遍历与递归迭代技术
题目一 剑指 Offer 32 - I. 从上到下打印二叉树 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/cong-shang-dao-xi ...