
在 Android 10 及以上系统中,仅声明 BLUETOOTH_SCAN 和 BLUETOOTH_CONNECT 权限不足以通过 check_permission() 验证;必须额外适配运行时权限模型,并显式请求 ACCESS_FINE_LOCATION(因蓝牙扫描被视为位置敏感操作)。
在 android 10 及以上系统中,仅声明 `bluetooth_scan` 和 `bluetooth_connect` 权限不足以通过 `check_permission()` 验证;必须额外适配运行时权限模型,并显式请求 `access_fine_location`(因蓝牙扫描被视为位置敏感操作)。
Android 从 API level 29(Android 10)起对蓝牙扫描实施了严格的隐私管控:即使设备未开启 GPS,蓝牙扫描也被归类为“基于位置的操作”,因此系统强制要求应用同时持有 ACCESS_FINE_LOCATION 权限,且该权限必须在运行时动态授予(而非仅在 buildozer.spec 中声明)。这正是你遇到“WiFi 权限检查正常、蓝牙却始终返回 False”的根本原因——check_permission() 严格遵循 Android 的实际授予权限状态,而你的 App 在 Android 10+ 设备上并未真正获得 ACCESS_FINE_LOCATION 的运行时授权(尽管安装时可能已声明)。
✅ 正确的权限检查与请求流程
你需要按顺序检查并请求全部必要权限,尤其注意 ACCESS_FINE_LOCATION 必须在蓝牙权限之前或同时被授予:
from android.permissions import check_permission, request_permissions, Permission
from android import mActivity
def request_bluetooth_permissions():
permissions = [
Permission.ACCESS_FINE_LOCATION, # 必须首先确保此权限已授
Permission.BLUETOOTH_SCAN,
Permission.BLUETOOTH_CONNECT,
]
# 检查是否全部已授予
if all(check_permission(p) for p in permissions):
print("✅ All Bluetooth permissions granted.")
return True
# 否则发起批量请求(注意:Android 12+ 要求分组请求,但 Pyjnius 当前版本建议一次性请求)
def on_permission_result(permissions_list, grants):
if all(grants):
print("✅ Permissions granted successfully.")
else:
print("❌ Some permissions denied:",
[p for p, g in zip(permissions_list, grants) if not g])
request_permissions(permissions, on_permission_result)
return False
# 在按钮回调中调用
def on_bluetooth_scan_button():
if request_bluetooth_permissions():
# ✅ 安全执行蓝牙扫描逻辑(建议仍在独立线程中)
from threading import Thread
Thread(target=perform_bluetooth_scan, daemon=True).start()
⚠️ 关键注意事项
-
buildozer.spec仍需完整声明(不可省略):android.permissions = ACCESS_FINE_LOCATION, BLUETOOTH_SCAN, BLUETOOTH_CONNECT, BLUETOOTH_ADMIN
注意:
BLUETOOTH_ADMIN虽非 Android 10+ 强制要求,但部分旧设备或后台扫描场景仍需它,建议保留。 ACCESS_FINE_LOCATION是硬性前提:即使你的蓝牙功能不涉及地理位置,Android 10+ 也强制将其与位置权限绑定。若用户拒绝该权限,BLUETOOTH_SCAN/CONNECT将永远无法通过check_permission()。不要依赖“安装时默认授权”:Android 6.0+(API 23)起,所有危险权限(包括
ACCESS_FINE_LOCATION)必须在运行时由用户明确点击“允许”,buildozer.spec中的声明仅用于安装时告知系统“本应用需要这些能力”。线程安全无影响:你提到 WiFi 和蓝牙扫描均在独立线程中执行——这本身不是问题根源。权限检查必须在主线程(UI 线程)进行,因为
request_permissions()会触发系统弹窗,而弹窗只能由 Activity 主线程发起。
? 总结
Bluetooth 权限检查在 Android 10+ 失效,本质是平台策略升级所致,而非代码或 Pyjnius 的 Bug。解决方案非常明确:将 ACCESS_FINE_LOCATION 视为蓝牙功能的前置依赖项,统一纳入运行时权限请求链,并确保用户完成该授权后再执行扫描逻辑。忽略这一变化,就会出现“代码逻辑正确、权限已声明、但 check_permission 始终返回 False”的典型现象。











