报错信息
在 Android 12 系统上真机执行 BLE 蓝牙设备扫描时,APP 直接崩溃,Logcat 中报错:
java.lang.SecurityException: Need android.permission.BLUETOOTH_SCAN permission for
android.content.AttributionSource@9e60bb10: GattService registerScanner
...
at com.st.BlueSTSDK.Manager.startBleScan_post21(Manager.java:283)
at com.st.BlueSTSDK.Manager.startBleScan(Manager.java:270)
at com.st.BlueSTSDK.Manager.startDiscovery(Manager.java:253)
at com.st.BlueSTSDK.Utils.NodeScanActivity.startNodeDiscovery(NodeScanActivity.java:99)
at com.sunzhongwei.bluevoice.NodeListActivity.startNodeDiscovery(NodeListActivity.java:268)
at com.sunzhongwei.bluevoice.NodeListActivity.onResume(NodeListActivity.java:183)
对应代码
@Override
protected void onResume() {
resetNodeList();
startNodeDiscovery();
super.onResume();
}//onListViewIsDisplayed
注释掉 startNodeDiscovery 就能看到界面,说明权限申请有问题。
注意:
Activity 的生命周期中,onResume 是在 onStart 之后执行的。
- onCreate
- onStart
- onResume
参考:
https://developer.android.com/guide/components/activities/activity-lifecycle
修改 manifest
添加权限
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
运行时申请蓝牙权限
在函数 startNodeDiscovery() 中添加:
if (ContextCompat.checkSelfPermission(NodeListActivity.this, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_DENIED)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
{
ActivityCompat.requestPermissions(NodeListActivity.this, new String[]{Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN}, 2);
return;
}
}
添加权限申请之后,再次打开 APP,就会看到弹窗提示申请权限授权。
会看到一个有趣的地方,权限的格式 Manifest.permission.BLUETOOTH_SCAN 都是 Manifest 开头的。这是因为 ActivityCompat.requestPermissions 只能申请在 Manifest 中配置的权限。
申请权限的异步问题
ActivityCompat.requestPermissions 这种弹窗确认操作,通常是异步执行的。
是否可以在获取权限之后,继续之后的操作,避免用户去点击两次。即,找到申请权限成功的回调。
If your app does not have the requested permissions the user will be presented with UI for accepting them. After the user has accepted or rejected the requested permissions you will receive a callback reporting whether the permissions were granted or not. Your activity has to implement and the results of permission requests will be delivered to its onRequestPermissionsResult method.
onRequestPermissionsResult:Callback for the result from requesting permissions. This method is invoked for every call on requestPermissions.
权限申请的异步检查:
// 申请权限回调:判断成功/拒绝
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 2:
// 对应 ActivityCompat.requestPermissions 调用时的第三个参数值
Log.d(TAG, "onRequestPermissionsResult: " + Arrays.toString(permissions));
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// We have the permission
Log.d(TAG, "onRequestPermissionsResult: Permission granted");
} else {
Log.d(TAG, "onRequestPermissionsResult: Permission not granted");
}
break;
default:
Log.d(TAG, "onRequestPermissionsResult: All Permission Granted");
}
}
从日志上看,有两次返回:
onRequestPermissionsResult: [android.permission.BLUETOOTH_CONNECT, android.permission.BLUETOOTH_SCAN]
onRequestPermissionsResult: Permission granted
onRequestPermissionsResult: [android.permission.ACCESS_FINE_LOCATION]
onRequestPermissionsResult: Permission granted
对应的界面分别是:
我感觉提示信息容易引起误解,会让用户以为是申请定位信息,而不是连接蓝牙设备。
获取权限之后,为何自动扫描了。是 onResume 被自动触发了?
确实是这样:
When you show dialog of permission question, Acitvity goes to onPause, and when dialog hides, it goes to onResume.
参考
- https://stackoverflow.com/questions/67722950/android-12-new-bluetooth-permissions
- https://developer.android.com/reference/androidx/core/app/ActivityCompat
- https://stackoverflow.com/questions/43680921/permission-requests-causes-infinite-loop-in-onresume
微信关注我哦 👍
我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式