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)
            }
        })
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章