现在大部分手机都有蓝牙功能,可以与其他有蓝牙功能的设备连接、传输数据等。本文介绍如何使用蓝牙框架API扫描和配对附近的设备。
申请权限
如果要在App中使用蓝牙功能,需要申请多个权限,具体如下:
targetSdk 31(Android 12)及以上
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!--允许App连接到配对的蓝牙设备(兼容低版本)-->
<uses-permission
android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<!--允许App发现和配对蓝牙设备(兼容低版本)-->
<uses-permission
android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<!--若App需要扫描蓝牙设备,申请此权限-->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!--若App使当前设备可供其他蓝牙设备发现,申请此权限-->
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<!--若App与蓝牙设备配对、通讯,申请此权限-->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!--若App通过蓝牙扫描结果获取物理位置,申请此权限-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>
确定不会使用蓝牙扫描结果获取物理位置时,设置BLUETOOTH_SCAN
权限的usesPermissionFlags
,就无需再申请ACCESS_FINE_LOCATION
权限。
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!--若App需要扫描蓝牙设备,申请此权限-->
<!--当usesPermissionFlags设置为neverForLocation时,无需再申请ACCESS_FINE_LOCATION权限-->
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
</manifest>
需要注意的是,BLUETOOTH_SCAN
、BLUETOOTH_ADVERTISE
、BLUETOOTH_CONNECT
均为运行时权限,需要申请并获得用户同意后,才能扫描附近蓝牙设备、被其他蓝牙设备发现或与其他蓝牙设备配对和通信,代码如下:
class BluetoothExampleActivity : AppCompatActivity() {
private val requestMultiplePermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions: Map<String, Boolean> ->
val noGrantedPermissions = ArrayList<String>()
permissions.entries.forEach {
if (!it.value) {
noGrantedPermissions.add(it.key)
}
}
if (noGrantedPermissions.isEmpty()) {
// 所有申请权限通过,可以执行后续操作
} else {
//未同意授权
noGrantedPermissions.forEach {
if (!shouldShowRequestPermissionRationale(it)) {
//用户拒绝权限并且系统不再弹出请求权限的弹窗
//这时需要我们自己处理,比如自定义弹窗告知用户为何必须要申请这个权限
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val requestPermissionNames = arrayOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_ADVERTISE, Manifest.permission.BLUETOOTH_CONNECT)
if (requestPermissionNames.find { ActivityCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED } != null) {
requestMultiplePermissionLauncher.launch(requestPermissionNames)
} else {
// 所有申请权限通过,可以执行后续操作
}
}
}
targetSdk 30(Android 11)及以下
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!--允许App请求连接、接受连接或传输数据-->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<!--允许App发现和配对蓝牙设备-->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!--在Android 11(30)及以下版本中,位置权限是必须的-->
<!--在Android 9(28)及以下版本中,可以使用ACCESS_COARSE_LOCATION权限替代ACCESS_FINE_LOCATION权限-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>
需要注意的是,位置权限为运行时权限,需要申请并获得用户同意后,才能扫描附近蓝牙设备,代码如下:
class BluetoothExampleActivity : AppCompatActivity() {
private val requestPermissionName = Manifest.permission.ACCESS_FINE_LOCATION
private val requestSinglePermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted: Boolean ->
if (granted) {
// 申请权限通过,可以执行后续操作
} else {
if (!shouldShowRequestPermissionRationale(requestPermissionName)) {
//用户拒绝权限并且系统不再弹出请求权限的弹窗
//这时需要我们自己处理,比如自定义弹窗告知用户为何必须要申请这个权限
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (ActivityCompat.checkSelfPermission(this, requestPermissionName) == PackageManager.PERMISSION_GRANTED) {
// 申请权限通过,可以执行后续操作
} else {
requestSinglePermissionLauncher.launch(requestPermissionName)
}
}
}
检查与开启蓝牙
检查是否支持蓝牙功能
要使用蓝牙功能,首先需要确定当前设备是否支持蓝牙功能,代码如下:
class BluetoothExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val bluetoothAdapter = (getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
if (bluetoothAdapter != null) {
// 当前设备支持蓝牙功能
}
}
}
开启蓝牙
确认当前设备支持蓝牙功能后,检测蓝牙是否开启,未开启的话可以调用系统方法开启蓝牙,代码如下:
class BluetoothExampleActivity : AppCompatActivity() {
private val intentLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
if (activityResult.resultCode == Activity.RESULT_OK) {
// 成功开启蓝牙
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val bluetoothAdapter = (getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
if (bluetoothAdapter != null) {
if (bluetoothAdapter.isEnabled != true) {
// 蓝牙未开启,通过系统启用蓝牙
intentLauncher.launch(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE))
}
}
}
}
扫描和配对附近的设备
权限已申请完毕、蓝牙功能开启后,可以扫描附近的设备,进行配对。
获取已配对过的设备
当前设备可能已经与某些蓝牙设备配对过,获取已配对过的设备的代码如下:
class BluetoothExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val bluetoothAdapter = (getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
// 已绑定的设备
val bondedDevices: Set<BluetoothDevice>? = bluetoothAdapter?.bondedDevices
}
}
扫描附近设备并进行配对
注册广播接收者,监听扫描结果,使用startDiscovery
扫描附近其他蓝牙设备。可以使用BluetoothDevice
的createBond
方法进行配对,代码如下:
class BluetoothExampleActivity : AppCompatActivity() {
private val scanResultReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
BluetoothDevice.ACTION_FOUND -> {
// 发现的蓝牙设备
val device: BluetoothDevice? = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
// 如果已经找到想要的设备,可以停止扫描
bluetoothAdapter?.cancelDiscovery()
// 与发现的蓝牙设备配对
device?.createBond()
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 注册蓝牙设备扫描结果监听
registerReceiver(scanResultReceiver, IntentFilter().apply {
addAction(BluetoothDevice.ACTION_FOUND)
})
val bluetoothAdapter = (getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
bluetoothAdapter?.startDiscovery()
}
override fun onDestroy() {
super.onDestroy()
// 取消扫描结果监听
unregisterReceiver(scanResultReceiver)
}
}
示例
效果如图:
完整演示代码已在示例Demo中添加。
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END