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