
在 Android 10 及以上系统中,Kivy/Pyjnius 应用调用 check_permission() 检测蓝牙权限(如 BLUETOOTH_SCAN)会始终返回 False,即使已声明并请求过权限——这是因 Android 权限模型升级导致的兼容性问题,需按新版运行时权限规范适配。
在 android 10 及以上系统中,kivy/pyjnius 应用调用 `check_permission()` 检测蓝牙权限(如 `bluetooth_scan`)会始终返回 `false`,即使已声明并请求过权限——这是因 android 权限模型升级导致的兼容性问题,需按新版运行时权限规范适配。
Android 自 API 31(Android 12)起对蓝牙权限进行重大重构,而从 API 29(Android 10)开始已引入前置限制:BLUETOOTH_SCAN、BLUETOOTH_CONNECT 和 BLUETOOTH_ADVERTISE 不再属于传统危险权限组,而是被归类为 特殊运行时权限(Special Permissions),其检测与申请机制与 ACCESS_FINE_LOCATION 等标准危险权限完全不同。
? 为什么 check_permission() 对蓝牙权限“失效”?
-
android.permissions.check_permission()仅支持标准危险权限(如ACCESS_FINE_LOCATION、CAMERA),底层调用的是Context.checkSelfPermission()。 - 而
BLUETOOTH_SCAN等在 Android 10+ 中需通过PackageManager.canRequestPackagePermissions()或ActivityResultLauncher(AndroidX)判断,无法用checkSelfPermission()检测 —— 因此check_permission()永远返回False,属于预期行为,而非 Bug。
✅ 正确做法:区分权限类型 + 使用 request_permissions() 主动申请
你已在 buildozer.spec 和 request_permissions.py 中声明权限,这是必要但不充分的步骤。关键在于:必须显式调用 request_permissions() 并等待用户授权回调,而非依赖 check_permission() 判断结果。
✅ 推荐代码结构(适配 Android 10+)
from android.permissions import request_permissions, Permission, check_permission
from kivy.clock import mainthread
def start_bluetooth_scan(self):
# 1. 先检查 LOCATION(基础前提,蓝牙扫描需定位权限)
if not check_permission(Permission.ACCESS_FINE_LOCATION):
request_permissions([Permission.ACCESS_FINE_LOCATION], self.on_location_granted)
return
# 2. 再请求蓝牙相关权限(Android 10+ 必须显式申请)
bluetooth_perms = [
Permission.BLUETOOTH_SCAN,
Permission.BLUETOOTH_CONNECT,
# Permission.BLUETOOTH_ADVERTISE, # 如需广播才启用
]
request_permissions(bluetooth_perms, self.on_bluetooth_granted)
@mainthread
def on_location_granted(self, permissions, grants):
if all(grants):
# 位置权限已获准,继续申请蓝牙权限
self.start_bluetooth_scan()
else:
self.show_error("定位权限被拒绝,无法启用蓝牙扫描")
@mainthread
def on_bluetooth_granted(self, permissions, grants):
# 注意:grants 是布尔列表,需全部为 True 才视为成功
if all(grants):
print("✅ 蓝牙权限已授予,启动扫描...")
# 在此处调用你的蓝牙扫描逻辑(建议仍置于线程中,但权限检查已完成)
self._start_scan_in_thread()
else:
self.show_error("蓝牙权限未被完全授予,请在系统设置中手动开启")⚠️ 关键注意事项
-
ACCESS_FINE_LOCATION是硬性前提:即使仅扫描蓝牙设备(不获取位置),Android 10+ 仍强制要求该权限,且必须在蓝牙权限之前申请。 -
不要依赖
check_permission()判断蓝牙权限状态:它永远返回False,应以request_permissions()的回调结果为准。 -
用户可能手动关闭权限:即使首次授权成功,后续可在「设置 → 应用 → 权限管理」中单独关闭
BLUETOOTH_SCAN,此时需再次触发request_permissions()。 -
buildozer.spec配置必须完整:android.permissions = android.permission.ACCESS_FINE_LOCATION, \ android.permission.BLUETOOTH_SCAN, \ android.permission.BLUETOOTH_CONNECT, \ android.permission.BLUETOOTH_ADVERTISE android.api = 33 # 建议 ≥31 android.ndk = 25c
? 总结
WiFi 权限(基于 ACCESS_FINE_LOCATION)工作正常,是因为它属于传统危险权限;而蓝牙新权限是 Android 架构演进的结果,需主动申请、异步回调、手动容错。放弃对 check_permission() 的盲目信任,转向以 request_permissions() 为核心、以回调驱动的权限流,是 Kivy/Pyjnius 在 Android 10+ 上稳定使用蓝牙功能的唯一可靠路径。


















