Android踩坑之解决蓝牙无法回调问题-Kotlin示例
Android 6.0之后,蓝牙扫描回调需要获取模糊定位查询,Android 10之后更严格,需要获取精确定位。
这些年Google对安卓的控制可谓是越来越严谨了,安全性也是越来越高。
现在的问题是,当你的targetSDK>22的时候,扫描蓝牙就不不会有回调了,而且即使是在Manifest中添加了permission也依然无法获取回调,解决办法如下:
TargetSdk降级到22——降级法
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.hicling.iictcling"
minSdkVersion 18
targetSdkVersion 22
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
ndk {
// 设置支持的SO库架构,第三方给的so库哪几种架构,就配置这几种架构
abiFilters 'armeabi' , 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
}
}
.......
以上办法比较愚蠢,会导致app的目标sdk过老旧,手机可能会提示兼容性问题,很不靠谱,建议仅仅是要解决问题的偷懒可以这个干,真要解决这个蓝牙回调权限问题请参照法二,如下:
开启权限
第一部,修改Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.hicling.iictcling">
<!--关键代码-->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true"
tools:targetApi="q">
第二部,手动开启权限的kotlin代码(注意android10开始,这个蓝牙permission通过manifest已经无法激活成功了,需要手动向用户提示,让用户打开哦。
/**
* 解决:无法发现蓝牙设备的问题
*/
private val accessCode = 101
private val permissions: Array<String> = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
private var countRequest = 0
// get bluetooth permission
private fun getBlePermission() {
countRequest++
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
var permissionCheck = checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
permissionCheck += checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
requestPermissions(permissions, accessCode)
}
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
accessCode -> if (checkPermission(grantResults)) {
Log.i(tag, "onRequestPermissionsResult: 用户允许权限 accessCode:$accessCode")
} else {
Log.i(tag, "onRequestPermissionsResult: 拒绝搜索设备权限 accessCode:$accessCode")
if (countRequest > 2) {
// ask User to grant permission manually
AlertDialog.Builder(this)
.setMessage(R.string.open_permission_req)
.setTitle(R.string.request)
.setPositiveButton(R.string.confirm) { _, _ ->
goIntentSetting()
}.create().show()
} else getBlePermission()
}
}
}
private fun checkPermission(grantResults: IntArray): Boolean {
for (grantResult in grantResults) {
if (grantResult == PackageManager.PERMISSION_DENIED) {
return false
}
}
return true
}
// open app settings let user grant the permission
private fun goIntentSetting() {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri: Uri = Uri.fromParts("package", this.packageName, null)
intent.data = uri
try {
this.startActivity(intent)
} catch (e: Exception) {
e.printStackTrace()
}
}
注意上面有个打开app设置的方法,因为如果用户两次拒绝permission的话,app将不再弹出获取permission的提示,需要用户从setting中打开,故而在上述代码逻辑中判断用户是否已经被两次拒绝,如果是将会打开setting设置,催促用户手动打开权限。
Example
以下是扫描蓝牙设备的样例代码,仅供参考。
mWebView.registerHandler("scanBle", WVJBWebView.WVJBHandler<Any?, Any?> { data, function ->
Log.i(tag, "js call scanBle")
Log.i(tag, data.toString())
val data = Gson().fromJson(data.toString(), TimeData::class.java)
if (checkBle()) {
val scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult?) {
super.onScanResult(callbackType, result)
val msg = "BleScan results"
Log.i(tag, msg)
Log.i(tag, result.toString())
function.onResult(json(1, result, msg))
}
override fun onBatchScanResults(results: MutableList<ScanResult>?) {
super.onBatchScanResults(results)
val msg = "Batch Scan Results"
Log.i(tag, msg)
Log.i(tag, results.toString())
function.onResult(json(1, results, msg))
}
override fun onScanFailed(errorCode: Int) {
super.onScanFailed(errorCode)
val msg = "BleScan fail to scan errorCode: $errorCode"
Log.i(tag, msg)
Log.i(tag, errorCode.toString())
function.onResult(json(0, null, msg))
}
}
val scanner = BluetoothAdapter.getDefaultAdapter().bluetoothLeScanner
Log.i(tag, "start scan for ${data.time / 1000}s")
scanner.startScan(scanCallback)
handler.postDelayed({
scanner.stopScan(scanCallback)
Log.i(tag, "stop scan ble")
}, data.time)
}
})