- 手记 | MobPush 接入总结
- Gradle 手记|盘点我使用过的 build 基本配置(不断更新中。。。
- Git 手记 | 总结个人日常开发常用到的一些命令(不断更新。。。
- Utils 手记 | Android Studio 插件整理(不断更新中...
- 手记|Kotlin 基础类型回顾
- 手记|Android 获取已配对蓝牙列表和已连接蓝牙名称
序
最近正好项目有蓝牙需求,折腾了小一会儿,特意记录一下~
网上好多搜索到的文章没法用,尴尬死。。。或者就是 Java 版本,转 Kotlin 多少有点坑,不过更加加深了要好好学习一下 Kotlin 的想法~
附上脑图:
Demo GitHub 地址:
蓝牙简述
蓝牙的出现,让移动设备多了一种交换数据的方式,而 Android 应用可以通过 Bluetooth Api 执行如下操作:
- 扫描其他蓝牙设备
- 查询本地蓝牙适配器的配对蓝牙设备
- 建立 RFCOMM 通道
- 通过服务发现连接到其他设备
- 与其他设备进行双向数据传输
- 管理多个连接
而我们今天,主要是进行第二点,查询本地蓝牙适配器的配对蓝牙设备。
传统的蓝牙适用于较为耗电的操作,其中包括 Android 设备之间的流式传输和通信等。而针对具有低功耗要求的蓝牙设备,Android 4.3(API 18)中引入面向低功耗蓝牙的 API 支持。
1、基础知识
为了让蓝牙设备可以在彼此之间传输数据,必须先通过配对过程形成通道。
其中一台设备需要将自身设置为可接收传入的连接请求,另一台设备则通过服务发现过程并找到可检测的设备。
随后在检测到的设备接受配对请求后,设备之间完成绑定操作,并在此期间交换安全密钥。二者会缓存这些密钥,以供日后使用。
完成配对和绑定过程后,两台设备会交换信息。
当会话完成时,发起配对请求的设备会将其链接到可检测设备的通道。
并且这俩台设备仍然保持绑定状态,因此在未来的会话期间,只要两者在彼此的范围内且均为移除绑定,便会自动连接。
2、蓝牙权限
基础权限必须声明:
<uses-permission android:name="android.permission.BLUETOOTH" />
如果想对蓝牙进行相关操作,比如打开蓝牙等,需要配置如下权限:
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
而当应用需要扫描其他设备时,需要声明如下权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
这里需要注意的:
- 如果应用适配 Android 9(Api 28)或者更低版本,则可以声明 ACCESS_COARSE_LOCATION 权限,而非 ACCESS_FINE_LOCATION 权限。
如果需要声明当前应用依然适用于不支持 BLE 的设备,则需要在权限中添加如下元素:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>
蓝牙 Demo 搞起(获取已配对列表以及已连接蓝牙名称)
1、必不可少的权限
<uses-permission android:name="android.permission.BLUETOOTH" />
2、工具类
package com.hlq.bluetoothpro.utils
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothProfile
import android.content.Context
import android.media.AudioManager
import android.util.Log
import java.lang.reflect.InvocationTargetException
/**
* @author:HLQ_Struggle
* @date:2021/8/29
* @desc:
*/
private var mBluetoothAdapter: BluetoothAdapter? = null
/**
* 实例化 BluetoothAdapter
*/
private fun getInstance(): BluetoothAdapter? {
if (mBluetoothAdapter == null) {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
}
return mBluetoothAdapter
}
/**
* 检测设备是否支持蓝牙
*/
fun checkBluetoothEnable(): Boolean {
return getInstance() == null
}
/**
* 判断当前蓝牙是否打开
*/
fun checkBluetoothStateEnable(): Boolean {
return getInstance()?.isEnabled == true
}
/**
* 获取蓝牙耳机连接状态
*/
private fun isWiredHeadsetConnected(context: Context): Boolean {
try {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
return audioManager.isWiredHeadsetOn
} catch (e: Exception) {
}
return false
}
/**
* 判断蓝牙耳机是否已连接
*/
fun hasBluetoothAudioDevice(): Boolean {
val adapter = BluetoothAdapter.getDefaultAdapter()
var a2dp = false
var headset = false
try {
a2dp =
adapter.getProfileConnectionState(BluetoothProfile.A2DP) != BluetoothProfile.STATE_DISCONNECTED
headset =
adapter.getProfileConnectionState(BluetoothProfile.HEADSET) != BluetoothProfile.STATE_DISCONNECTED
} catch (e: Throwable) {
}
return a2dp || headset
}
/**
* 获取到已配对成功蓝牙设备
*/
fun fetchAlReadyConnection() {
getInstance()?.let {
val devices = it.bondedDevices
for (device in devices) {
Log.e(
"HLQ", "----> " +
"name ${device.name} " +
"address ${device.address} " +
"bondState ${device.bondState} " +
"type ${device.type} ${device.uuids.size}"
)
}
}
}
fun getConnectedBtDevice(): String? {
//获取蓝牙适配器
val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
//得到已匹配的蓝牙设备列表
val bondedDevices = bluetoothAdapter.bondedDevices
if (bondedDevices != null && bondedDevices.size > 0) {
for (bondedDevice in bondedDevices) {
try {
//使用反射调用被隐藏的方法
val isConnectedMethod =
BluetoothDevice::class.java.getDeclaredMethod(
"isConnected"
)
isConnectedMethod.isAccessible = true
val isConnected =
isConnectedMethod.invoke(bondedDevice) as Boolean
if (isConnected) {
return bondedDevice.name
}
} catch (e: NoSuchMethodException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
} catch (e: InvocationTargetException) {
e.printStackTrace()
}
}
}
return null
}
3、创建监听蓝牙广播
package com.hlq.bluetoothpro.receiver
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
import com.hlq.bluetoothpro.utils.checkBluetoothStateEnable
import com.hlq.bluetoothpro.utils.fetchAlReadyConnection
import com.hlq.bluetoothpro.utils.getConnectedBtDevice
import com.hlq.bluetoothpro.utils.hasBluetoothAudioDevice
/**
* @author:HLQ_Struggle
* @date:2021/8/28
* @desc:
*/
class BluetoothReceiver : BroadcastReceiver() {
companion object {
fun registerIntentFilter(): IntentFilter {
val intentFilter = IntentFilter()
intentFilter.apply {
addAction(BluetoothAdapter.ACTION_STATE_CHANGED); // 蓝牙状态改变
addAction("android.bluetooth.BluetoothAdapter.STATE_OFF"); // 本地蓝牙适配器已关闭
addAction("android.bluetooth.BluetoothAdapter.STATE_ON"); // 本地蓝牙适配器已打开,可以使用
addAction(BluetoothDevice.ACTION_ACL_CONNECTED); // 已和远程设备建立 ACL 连接
addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); // 与远程设备 ACL 断开连接
priority = Int.MAX_VALUE
}
return intentFilter
}
}
override fun onReceive(context: Context?, intent: Intent?) {
val action = intent?.action
action ?: return
val bluetoothLog = when (action) {
BluetoothAdapter.ACTION_STATE_CHANGED -> { // 监听蓝牙状态
when (val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0)) {
BluetoothAdapter.STATE_TURNING_ON -> {
"STATE_TURNING_ON 蓝牙开启中"
}
BluetoothAdapter.STATE_ON -> {
"STATE_ON 蓝牙开启"
}
BluetoothAdapter.STATE_CONNECTING -> {
"STATE_CONNECTING 蓝牙连接中"
}
BluetoothAdapter.STATE_CONNECTED -> {
"STATE_CONNECTED 蓝牙已连接"
}
BluetoothAdapter.STATE_DISCONNECTING -> {
"STATE_DISCONNECTING 蓝牙断开中"
}
BluetoothAdapter.STATE_DISCONNECTED -> {
"STATE_DISCONNECTED 蓝牙已断开"
}
BluetoothAdapter.STATE_TURNING_OFF -> {
"STATE_TURNING_OFF 蓝牙关闭中"
}
BluetoothAdapter.STATE_OFF -> {
"STATE_OFF 蓝牙关闭"
}
else -> "ACTION_STATE_CHANGED EXTRA_STATE $state"
}
}
BluetoothDevice.ACTION_ACL_CONNECTED -> { // 蓝牙已连接
Log.e("HLQ", "----> ACTION_ACL_CONNECTED 蓝牙已连接 ") // 蓝牙已打开 且 已连接
Log.e("HLQ", "----> 蓝牙已打开且已连接")
Log.e("HLQ", "----> 输出已配对成功蓝牙列表")
Log.e("HLQ", "----> ${fetchAlReadyConnection()}")
"----> 当前连接蓝牙名称:${getConnectedBtDevice()}"
}
BluetoothDevice.ACTION_ACL_DISCONNECTED -> { // 蓝牙已断开
"ACTION_ACL_DISCONNECTED 蓝牙已断开"
}
else -> "action $action"
}
Log.e("HLQ", "----> bluetoothLog $bluetoothLog")
}
}
4、蓝牙注册、移除以及获取
package com.hlq.bluetoothpro
import android.content.IntentFilter
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.hlq.bluetoothpro.receiver.BluetoothReceiver
import com.hlq.bluetoothpro.utils.*
class MainActivity : AppCompatActivity() {
/**
* 蓝牙监听 行车模式
*/
private var mBluetoothFilter: IntentFilter? = null
private var mBluetoothReceiver: BluetoothReceiver? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
handleBluetooth()
}
private fun handleBluetooth() {
// 验证当前设备是否支持蓝牙 支持便进行初始化
if (!checkBluetoothEnable()) {
Log.e("HLQ", "----> 当前设备支持蓝牙")
initBluetooth()
if (checkBluetoothStateEnable() && hasBluetoothAudioDevice()) { // 蓝牙已打开 且 已连接
Log.e("HLQ", "----> 蓝牙已打开且已连接")
Log.e("HLQ", "----> 输出已配对成功蓝牙列表")
Log.e("HLQ", "----> ${fetchAlReadyConnection()}")
Log.e("HLQ", "----> 当前连接蓝牙名称:${getConnectedBtDevice()}")
}
}
}
/**
* 初始化行车模式 蓝牙监听
*/
private fun initBluetooth() {
if (mBluetoothReceiver == null) {
mBluetoothReceiver = BluetoothReceiver()
}
if (mBluetoothFilter == null) {
mBluetoothFilter = BluetoothReceiver.registerIntentFilter()
}
if (mBluetoothReceiver != null && mBluetoothFilter != null) {
registerReceiver(mBluetoothReceiver, mBluetoothFilter)
}
}
override fun onDestroy() {
super.onDestroy()
// 清理蓝牙广播
if (mBluetoothReceiver != null) {
unregisterReceiver(mBluetoothReceiver)
mBluetoothReceiver = null
}
}
}
来吧,日志展示
- 当蓝牙已打开且蓝牙已连接
E/HLQ: ----> 当前设备支持蓝牙
E/HLQ: ----> 输出已配对成功蓝牙列表
E/HLQ: ----> 蓝牙已打开且已连接
E/HLQ: ----> name Earbuds X1 address E0:9D:FA:CA:4C:DF bondState 12 type 1 3
E/HLQ: ----> 当前连接蓝牙名称:Earbuds X1
- 当蓝牙已打开且开始连接蓝牙(之前配对成功过,自动连接)
E/HLQ: ----> 当前设备支持蓝牙
E/HLQ: ----> 蓝牙已打开且已连接
E/HLQ: ----> 输出已配对成功蓝牙列表
E/HLQ: ----> name Earbuds X1 address E0:9D:FA:CA:4C:DF bondState 12 type 1 3
E/HLQ: ----> 当前连接蓝牙名称:Earbuds X1
- 再来个蓝牙开关展示
E/HLQ: ----> bluetoothLog STATE_TURNING_OFF 蓝牙关闭中
E/HLQ: ----> bluetoothLog STATE_OFF 蓝牙关闭
E/HLQ: ----> bluetoothLog STATE_TURNING_ON 蓝牙开启中
E/HLQ: ----> bluetoothLog STATE_ON 蓝牙开启
E/HLQ: ----> ACTION_ACL_CONNECTED 蓝牙已连接
E/HLQ: ----> 蓝牙已打开且已连接
E/HLQ: ----> 输出已配对成功蓝牙列表
E/HLQ: ----> name Earbuds X1 address E0:9D:FA:CA:4C:DF bondState 12 type 1 3
E/HLQ: ----> kotlin.Unit
E/HLQ: ----> bluetoothLog ----> 当前连接蓝牙名称:Earbuds X1
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。