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)
}
})