项目基线搭建:目标与最低 SDK 选择、build.gradle 配置、manifest 权限声明
章节目标
本项目基线的搭建是后续所有蓝牙 API 向后兼容实践的起点。本章将指导你确定最低支持 SDK 版本、合理设定 targetSdk 和 compileSdk,配置模块级 build.gradle 文件,并精确声明蓝牙相关权限(包括经典蓝牙、BLE 及 Android 10 / 12 / 13 引入的新权限模型)。完成本节后,你将得到一个可在 5 个 Android 版本以上稳定运行、权限声明无冗余无缺失的工程基线。
原理:为什么基线搭建决定向后兼容成败
Android 蓝牙 API 从 API 18(Android 4.3)开始提供 BLE 支持,但经典蓝牙的 BluetoothAdapter 自 API 5 就已经存在。然而权限模型在以下版本发生了断裂式变化:
- Android 10 (API 29):广播扫描和位置权限绑定,
ACCESS_FINE_LOCATION变为扫描 BLE 的必要条件。 - Android 12 (API 31):引入
BLUETOOTH_SCAN、BLUETOOTH_ADVERTISE、BLUETOOTH_CONNECT运行时权限,旧版BLUETOOTH/BLUETOOTH_ADMIN不再用于运行时检查。 - Android 13 (API 33):进一步细化,后台扫描需
ACCESS_BACKGROUND_LOCATION+ 运行时权限组合。
如果 minSdk 过低但 targetSdk 过高,应用会在低版本设备上请求不存在的权限;反之,若 minSdk 过高则丢失大量用户。基线搭建的核心是找到“向下兼容”与“向上适配”的平衡点。
实现流程(6 步)
- 确定最低 SDK:经典蓝牙 + BLE 支持建议设为
minSdk = 21(Android 5.0),既能覆盖 95% 以上活跃设备,又避免处理 API 18~20 的碎片化 BLE 适配。 - 选择编译 SDK:始终使用最新稳定版(如
compileSdk = 34),以获得最新 API 声明。 - 设定 targetSdk:指向当前最新版(34),通知系统应用已适配新权限行为。
- 配置 build.gradle:添加
androidx.core:core依赖以使用ActivityCompat运行时权限请求。 - 声明 manifest 权限:按
maxSdkVersion限制旧权限,使用uses-permission-sdk-23和android:maxSdkVersion控制权限注入范围。 - 声明 uses-feature:明确 BLE 或经典蓝牙硬件要求,防止 Google Play 过滤。
build.gradle 配置示例
模块级 build.gradle(仅展示关键部分)
android {
compileSdk 34
defaultConfig {
applicationId "com.example.blebackcompat"
minSdk 21
targetSdk 34
versionCode 1
versionName "1.0.0"
}
}
dependencies {
implementation 'androidx.core:core:1.12.0'
// 如需使用 BluetoothLeScanner 的 API 32+ 新方法,建议同时添加:
implementation 'androidx.activity:activity:1.8.0' // 用于 registerForActivityResult
}
manifest 权限声明(向后兼容写法)
以下配置同时兼容 Android 9(API 28)至 Android 14(API 34)。注意使用 android:maxSdkVersion 和 uses-permission-sdk-23:
<!-- 经典蓝牙(所有版本都需要,但 API 31+ 不再用于运行时) -->
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="32" />
<!-- Android 12+ 细粒度运行时权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<!-- 位置权限(BLE 扫描需要,API 29+ 为运行时,且 API 31+ 可声明 neverForLocation 免除) -->
<!-- 如果应用不使用 BLE 扫描位置,请添加 neverForLocation 属性 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
android:maxSdkVersion="30" />
<!-- 硬件特性声明:告诉 Google Play 应用需要 BLE 功能 -->
<uses-feature android:name="android.hardware.bluetooth_le"
android:required="false" />
<uses-feature android:name="android.hardware.bluetooth"
android:required="false" />
调试与验证
- 终端检查 manifest 合并结果:
./gradlew processDebugManifest --stacktrace # 查看输出文件 app/build/intermediates/merged_manifests/debug/AndroidManifest.xml - 运行时权限检查:使用
adb shell dumpsys package com.example.blebackcompat查看授予的权限列表。 - 模拟低版本设备:在 API 28、29、30、31 模拟器上分别测试权限请求对话框是否正常弹出。
常见错误与修复
| 错误场景 | 原因 | 解决方案 |
|---|---|---|
编译时报错 BLUETOOTH_SCAN 未找到 | compileSdk < 31 | 将 compileSdk 提升至 31+ |
| Android 12 设备上 BLE 扫描无回调 | 缺少 BLUETOOTH_SCAN 运行时权限 | 在 onRequestPermissionsResult 中处理新权限 |
| Google Play 拒绝上架 | ACCESS_FINE_LOCATION 未声明 maxSdkVersion | 添加 android:maxSdkVersion="30" 限制 |
低版本设备闪退:找不到 BluetoothLeScanner | 未做 API 级别检查 | 运行时判断 Build.VERSION.SDK_INT >= 18 |
性能与稳定性优化
- 权限请求精简:仅当实际需要时才请求位置权限。设置
neverForLocation标志可让 Android 12+ 设备不弹出位置对话框。 - uses-feature 非必须:将
bluetooth_le和bluetooth的required设为false,避免无硬件的设备无法安装。 - manifest 合并工具:在依赖库中如果引入了冲突权限(例如某些广告 SDK 声明了
ACCESS_BACKGROUND_LOCATION),使用tools:remove="android:name"移除。
实践要点: 确保
minSdk 不低于 21,targetSdk 和 compileSdk 指向最新版。在 manifest 中使用 maxSdkVersion 将旧权限限制在 API 30 及以下,新权限仅用于 API 31+。这一基线能让你在后续章节的蓝牙扫描、连接、广播编码时,省去 80% 的版本判断逻辑。
项目基线搭建流程图
新手提示: 在 Android Studio 中创建新项目时,直接选择 “API 21: Android 5.0” 作为最低 SDK,并在创建后手动将 compileSdk 和 targetSdk 改为最新版。然后一次性复制上方 manifest 权限块到你的 AndroidManifest.xml 中,即可获得最干净的基线。