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

前言

前一阵帮别人做个蓝牙的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低功耗设备。
问题搞定, 主要是需要明白其中的原理。

你可能感兴趣的:(android技术,android,蓝牙)