Android 12应用崩溃:请求启用蓝牙触发SecurityException
应用在Android 12设备上调用蓝牙开启Intent时崩溃,抛出如下异常:
Fatal Exception: java.lang.SecurityException
Permission Denial: starting Intent { act=android.bluetooth.adapter.action.REQUEST_ENABLE cmp=com.android.settings/.bluetooth.RequestPermissionActivity } from ProcessRecord{b25524d 23206:com.actofitSmartScale/u0a409} (pid=23206, uid=10409) requires android.permission.BLUETOOTH_CONNECT
尽管AndroidManifest.xml中已声明BLUETOOTH_CONNECT权限,仍出现此问题。以下是相关配置:
Gradle文件配置
compileSdkVersion 32 defaultConfig { applicationId "com" minSdkVersion 21 targetSdkVersion 30 versionCode 165 versionName "7.0.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" buildTypes { debug { versionNameSuffix ".debug" debuggable true applicationIdSuffix ".xxxx" } release { applicationIdSuffix ".xxxx" signingConfig signingConfigs.release } } }
AndroidManifest.xml配置
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.XXXXX.XXXXX"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <queries> <package android:name="com.google.android.apps.fitness" /> </queries> <queries> <intent> <action android:name="android.media.action.IMAGE_CAPTURE" /> </intent> </queries> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" /> <application android:name=".app.ApplicationClass" android:allowBackup="false" android:configChanges="orientation|screenSize|layoutDirection" android:icon="@mipmap/ic_smartscale_icon" android:label="@string/app_name" android:largeHeap="true" android:networkSecurityConfig="@xml/network_security_config" android:requestLegacyExternalStorage="true" android:supportsRtl="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true" tools:replace="android:allowBackup"> <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> </provider> <service android:name="com.clevertap.android.sdk.pushnotification.fcm.FcmMessageListenerService" android:exported="true"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service> <activity android:name=".app.BaseActivity" android:exported="true" android:screenOrientation="portrait" android:windowSoftInputMode="adjustPan"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <action android:name="android.intent.action.VIEW" /> </intent-filter> <nav-graph android:value="@navigation/nav_app" /> </activity> <activity android:name=".share.ShareActivity" android:exported="true" android:screenOrientation="landscape"> <intent-filter> <action android:name="com.actofit.share.smartscale" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> </activity> <activity android:name="com.clevertap.android.sdk.InAppNotificationActivity" android:screenOrientation="portrait" android:theme="@android:style/Theme.Translucent.NoTitleBar" /> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> <meta-data android:name="CLEVERTAP_ACCOUNT_ID" android:value="8R7-549-KR5Z" /> <meta-data android:name="CLEVERTAP_TOKEN" android:value="c45-a2b" /> <meta-data android:name="CLEVERTAP_USE_GOOGLE_AD_ID" android:value="1" /> <meta-data android:name="FCM_SENDER_ID" android:value="948450444651" /> <service android:name="com.qingniu.qnble.scanner.BleScanService" android:permission="android.permission.BIND_JOB_SERVICE" /> <service android:name="com.qingniu.scale.measure.ble.ScaleBleService" android:permission="android.permission.BIND_JOB_SERVICE" /> <service android:name="com.qingniu.scale.measure.broadcast.ScaleBroadcastService" android:permission="android.permission.BIND_JOB_SERVICE" /> <service android:name="com.qingniu.scale.wsp.ble.ScaleWspBleService" android:permission="android.permission.BIND_JOB_SERVICE" /> <service android:name="com.qingniu.scale.measure.broadcast.ScaleFoodBroadcastService" android:permission="android.permission.BIND_JOB_SERVICE" /> <service android:name=".biosense.BLeService" android:exported="false" /> <service android:name=".util.fcm.FcmMessageListenerService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service> <receiver android:name=".util.bluetooth.BluetoothBroadcastReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.bluetooth.adapter.action.STATE_CHANGED" /> </intent-filter> </receiver> <activity android:name="com.actofit.smartscale.bot.activity.ChatBotActivity" android:screenOrientation="portrait" /> <activity android:name="com.actofit.smartscale.payment.MakePaymentActivity" android:screenOrientation="portrait" /> <activity android:name="com.actofit.smartscale.share_report.ShareReportActivity" android:screenOrientation="portrait" /> <activity android:name="com.actofit.smartscale.dite.ActivitySearchFood" android:screenOrientation="portrait" /> <activity android:name="com.actofit.smartscale.dite.ActivityFoodInfo" android:screenOrientation="portrait" /> <activity android:name=".custom_webview.customCrome.WebviewActivity" android:screenOrientation="portrait" /> <activity android:name=".kitchin_scale.KitchinScaleTakeReadingActivity" android:screenOrientation="portrait" /> <activity android:name="com.actofit.smartscale.offline_support.QuestionActivity" android:exported="true" /> <activity android:name="com.actofit.smartscale.settings.GoalActivity" android:exported="true" /> <activity android:name="com.actofit.smartscale.home.LogActivity" android:exported="true" /> <activity android:name="com.actofit.smartscale.workout.OnBoardingActivity" android:configChanges="keyboardHidden|orientation|screenSize" /> <activity android:name="com.actofit.smartscale.workout.GuidedWorkoutActivity" android:exported="true" /> <activity android:name="com.actofit.smartscale.workout.RatingActivity" android:exported="true" /> <meta-data android:name="CLEVERTAP_BACKGROUND_SYNC" android:value="1" /> <receiver android:name=".util.fcm.NotificationBroadcastReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND"> <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <action android:name="com.google.android.c2dm.intent.REGISTRATION" /> <action android:name="android.service.notification.NotificationListenerService" /> </intent-filter> </receiver> </application> </manifest>
解决方案
1. 动态申请BLUETOOTH_CONNECT权限
Android 12(API 31及以上)将BLUETOOTH_CONNECT列为危险权限,仅在Manifest中声明无法生效,必须在代码中动态申请,且要在调用蓝牙开启Intent之前完成权限校验。
示例代码:
private static final int REQUEST_BLUETOOTH_CONNECT = 100; private static final int REQUEST_ENABLE_BT = 101; // 检查并申请蓝牙连接权限 private void checkBluetoothConnectPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.BLUETOOTH_CONNECT}, REQUEST_BLUETOOTH_CONNECT); return; } } // 权限已获取,启动蓝牙开启请求 startBluetoothEnableIntent(); } // 启动蓝牙开启Intent private void startBluetoothEnableIntent() { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); } // 处理权限申请结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_BLUETOOTH_CONNECT) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { startBluetoothEnableIntent(); } else { // 权限被拒绝,提示用户需要该权限才能使用蓝牙功能 Toast.makeText(this, "需要蓝牙连接权限才能开启蓝牙", Toast.LENGTH_SHORT).show(); } } }
2. 升级targetSdkVersion至31+
当前项目targetSdkVersion为30,虽然Android 12设备会向下兼容,但升级到31或更高版本能更好地适配Android 12的权限规则,避免潜在的兼容性问题。修改Gradle配置:
defaultConfig { // ...其他配置 targetSdkVersion 31 // 或更高版本 }
3. 确认权限申请时机
确保在任何调用ACTION_REQUEST_ENABLE Intent的代码路径前,都先执行权限检查逻辑,不要遗漏任何场景。
内容的提问来源于stack exchange,提问作者Rohit Barge

