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)
            }
        })
posted @ 2020-06-11 17:54  devilyouwei  阅读(1416)  评论(0编辑  收藏  举报