欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android BLE程序在Android 10手机搜不到设备问题分析

程序员文章站 2022-03-10 22:43:32
前言前一阵帮别人做个蓝牙的Android程序,调试的好好的, 用的自己的老手机,android 5.1、8.0的都是好好的能够搜索,但是交付过去,对方的华为p30 Android 10手机就是不行,这是什么原因呢,一阵挠头。。分析如要查找 BLE 设备,请使用 startLeScan() 方法。此方法将 BluetoothAdapter.LeScanCallback 作为参数。您必须实现此回调,因为这是返回扫描结果的方式。代码示例如下为扫描代码。/** * Activity for scan...

前言

前一阵帮别人做个蓝牙的Android程序,调试的好好的, 用的自己的老手机,android 5.1、8.0的都是好好的能够搜索,但是交付过去,对方的华为p30 Android 10手机就是不行,这是什么原因呢,一阵挠头。。

分析

如要查找 BLE 设备,请使用 startLeScan() 方法。此方法将 BluetoothAdapter.LeScanCallback 作为参数。您必须实现此回调,因为这是返回扫描结果的方式。

代码示例

如下为扫描代码。

/**
 * Activity for scanning and displaying available BLE devices.
 */
public class DeviceScanActivity extends ListActivity {

    private BluetoothAdapter bluetoothAdapter;
    private boolean mScanning;
    private Handler handler;

    // Stops scanning after 10 seconds.
    private static final long SCAN_PERIOD = 10000;
    ...
    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    bluetoothAdapter.stopLeScan(leScanCallback);
                }
            }, SCAN_PERIOD);

            mScanning = true;
            bluetoothAdapter.startLeScan(leScanCallback);
        } else {
            mScanning = false;
            bluetoothAdapter.stopLeScan(leScanCallback);
        }
        ...
    }
...
}

加入需要的权限:

<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
<uses-feature android:name="android.bluetooth.le" android:required="true"/>

注意,权限中的android.permission.ACCESS_COARSE_LOCATION是Android 6.0以上需要添加的权限。具体原因是蓝牙可以用来获取到模糊定位,所以需要请求用户同意。

if (Build.VERSION.SDK_INT >= 23) {
            int checkCallPhonePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION);
            if(checkCallPhonePermission != PackageManager.PERMISSION_GRANTED){
                //判断是否需要 向用户解释,为什么要申请该权限
                if(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_COARSE_LOCATION))
                    Toast.makeText(this,R.string.ble_need_location, Toast.LENGTH_LONG).show();

                ActivityCompat.requestPermissions(this ,new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},1);
                return;
            }else{

            }
        } 

官方解释

但是呢,问题其实也出在这里,在Android的官方文档里,低功耗蓝牙在Android 10上需要的权限已经改变了。
官方说明见https://developer.android.google.cn/guide/topics/connectivity/bluetooth-le

要在您的应用中使用蓝牙功能,您必须声明 BLUETOOTH 蓝牙权限。您需要此权限才能执行任何蓝牙通信,例如请求连接、接受连接和传输数据等。

考虑到 LE 信标通常与位置相关联,您还须声明 ACCESS_FINE_LOCATION 权限。没有此权限,扫描将无法返回任何结果。

注意:如果您的应用适配 Android 9(API 级别 28)或更低版本,则您可以声明 ACCESS_COARSE_LOCATION 权限而非 ACCESS_FINE_LOCATION 权限。

这里的意思即是, Android9或更低版本可以用ACCES S_COARSE_LOCATION 但是目标是以上的,即Android 10则需要ACCESS_FINE_LOCATION 权限了。
然后进行测试,找了同事的vivo android 10的手机换成新权限,确实ok了。哇,那这样的话,我如何保证新旧android版本的兼容性呢。
继续分析。
这里很重要的一点是我的build.gradle 配置

compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
    *******
    minSdkVersion 18
    targetSdkVersion 29

这个配置其实也是主要原因,之前厂家提供的demo ok, 而我因为用的是新的编译版本,targetSdkVersion也是29 (Android 10).
这里就要引出另一个关于Android权限的问题。

自动调整权限

官方解释见https://developer.android.google.cn/guide/topics/security/permissions.html

随着时间的推移,平台中可能会加入新的限制,要想使用特定 API,您的应用可能必须请求之前不需要的权限。因为现有应用假设可随意获取这些API 应用的访问权限,所以 Android 可能会将新的权限请求应用到应用清单,以免在新平台版本上中断应用。Android 将根据为 targetSdkVersion 属性提供的值决定应用是否需要权限。如果该值低于在其中添加权限的版本,则 Android 会添加该权限。

例如,API 级别 4 中加入了 WRITE_EXTERNAL_STORAGE 权限,用以限制访问共享存储空间。如果您的 targetSdkVersion 为 3 或更低版本,则会向更新 Android 版本设备上的应用添加此权限。

注意:如果某权限自动添加到应用,则即使您的应用可能实际并不需要这些附加权限,Google Play 上的应用列表也会列出它们。

为避免这种情况,并且删除您不需要的默认权限,请始终将 targetSdkVersion 更新至最高版本。可在
Build.VERSION_CODES 文档中查看各版本添加的权限。

请仔细了解这段话,主要句子Android 将根据为 targetSdkVersion 属性提供的值决定应用是否需要权限。如果该值低于在其中添加权限的版本,则 Android 会添加该权限。 假设应用可以获取这些API权限,则Android根据targetSdkVersion 决定应用是否需要权限。 即如果我gradle配置targetSdkVersion是29, 那没办法,android以为你知道你在干嘛, 你就必须声明ACCESS_FINE_LOCATION权限。

不过,另一方面,如果该值低于在其中添加权限的版本,则 Android 会添加该权限。 如果该值低于29, 则Android自动给我们这个权限。 即如果我targetSdkVersion 低于29, android就直接给我们这个权限了,这样程序也能直接运行在Android 10的平台上。

验证

话不多说,经过上边的分析,把build.gralde的配置targetSdkVersion 降低,这里设为26,运行程序,立马扫描到了蓝牙BLE低功耗设备。
问题搞定, 主要是需要明白其中的原理。

本文地址:https://blog.csdn.net/caizehui/article/details/107875427