不同版本的 Android 系统申请蓝牙权限

概述:

动态获取权限说明:

  • 1.Android 6 至 Android 11 需要动态获取 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION 权限;
  • 2.Android 10 至 Android 11 蓝牙扫描需要开启 定位开关;
  • 3.Android 12 及以上 需要动态获取 BLUETOOTH_CONNECT 和BLUETOOTH_SCAN 权限;
   <!-- 基础蓝牙权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

    <!-- 位置权限(Android 6.0+ 需要) -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <!-- Android 11+ 新增的蓝牙扫描权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

    <!-- Android 12+ 需要的蓝牙连接权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

    <!-- 如果需要访问蓝牙设备信息 -->
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />

    <!-- 如果需要前台服务(适用于后台扫描) -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

1. Android 6.0(API 级别 23)以下

在 Android 6.0 之前,权限在应用安装时就会被授予,不需要在运行时请求权限。只需要在 AndroidManifest.xml 中声明所需的蓝牙权限即可。

   <!-- 蓝牙权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

2. Android 6.0(API 级别 23)到 Android 11(API 级别 30)

从 Android 6.0 开始,引入了运行时权限机制,部分危险权限需要在运行时请求。对于蓝牙功能,除了声明蓝牙权限外,还需要请求位置权限,因为蓝牙扫描可能会泄露用户的位置信息。

   <!-- 位置权限 -->
   <!--    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />-->
   <!--    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />-->
   <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" />
   <uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <!-- 蓝牙权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

3. Android 12(API 级别 31)及以上

从 Android 12 开始,引入了新的蓝牙权限 BLUETOOTH_CONNECT 和 BLUETOOTH_SCAN,分别用于连接蓝牙设备和扫描蓝牙设备。

  <!-- 位置权限 -->
  <!--    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />-->
  <!--    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />-->
  <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" />
  <uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <!-- 新的蓝牙权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

    <!-- 旧的蓝牙权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

权限集合 组装代码

  val permissionList = mutableListOf<String>()
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// Android 6.0到Android 11
      permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION)// 精确位置
      permissionList.add(Manifest.permission.ACCESS_COARSE_LOCATION)// 粗略位置
  }
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // Android 12及以上
      permissionList.add(Manifest.permission.BLUETOOTH_CONNECT)
      permissionList.add(Manifest.permission.BLUETOOTH_SCAN)
  }
  LogUtils.d("权限列表:$permissionList")
        
  PermissionX.init(this)
      .permissions(permissionList)
       .onExplainRequestReason { scope, deniedList ->
           val message = "蓝牙使用需要同意以下权限才能正常使用"
           scope.showRequestReasonDialog(deniedList, message, "允许", "拒绝")
       }
       .onForwardToSettings { scope, deniedList ->
           scope.showForwardToSettingsDialog(
               deniedList, "定位权限设置", "ok", "ignore"
           )
       }
       .request { allGranted, grantedList, deniedList -> }

权限申请完整代码

BasePermissionActivity.kt

package com.aimed.measure.ui.measure

import android.Manifest
import android.app.AlertDialog
import android.bluetooth.BluetoothAdapter
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.location.LocationManager
import android.os.Build
import android.provider.Settings
import androidx.viewbinding.ViewBinding
import com.aimed.measure.R
import com.aimed.measure.base.BaseVBActivity
import com.aimed.measure.ext.showToast
import com.aimed.measure.utils.GpsUtil
import com.aimed.measure.utils.LogUtils
import com.aimed.measure.utils.StringUtils
import com.kl.minttisdk.ble.BleConstants
import com.permissionx.guolindev.PermissionX


abstract class BasePermissionActivity<VB : ViewBinding> : BaseVBActivity<VB>() {
    private var permissionCallback: (() -> Unit)? = null
    private val permissionList = mutableListOf<String>()
    var isNeedPermission = false

	// 统设置页面通常不会返回结果数据,导致 onActivityResult() 不被调用,所以采用广播。
    private val gpsStatusReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            if (LocationManager.PROVIDERS_CHANGED_ACTION == intent.action) {
                LogUtils.d("GPS已开启")
                if (GpsUtil.isGpsEnable(this@BasePermissionActivity)) {
                    if (isBluetoothEnable()) {
                        LogUtils.d("蓝牙已开启")
                        isNeedPermission = true
                        permissionCallback?.invoke()
                    } else {
                        //打开蓝牙
                        enableBluetooth()
                    }
                } else {
                    showToast("位置信息开启失败,请手动开启")
                }
            }
        }
    }

    fun requestPermission(callback: (() -> Unit)?) {
        permissionCallback = callback
		val permissionList = mutableListOf<String>()
        permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// Android 6.0到Android 11
            permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION)// 精确位置
            permissionList.add(Manifest.permission.ACCESS_COARSE_LOCATION)// 粗略位置
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // Android 12及以上
            permissionList.add(Manifest.permission.BLUETOOTH_CONNECT)
            permissionList.add(Manifest.permission.BLUETOOTH_SCAN)
        }
        LogUtils.d("权限列表:$permissionList")
        LogUtils.d("SDK版本:${Build.VERSION.SDK_INT}")

        PermissionX.init(this)
            .permissions(permissionList)
            .onExplainRequestReason { scope, deniedList ->
                val message = "蓝牙使用需要同意以下权限才能正常使用"
                scope.showRequestReasonDialog(deniedList, message, "允许", "拒绝")
            }
            .onForwardToSettings { scope, deniedList ->
                scope.showForwardToSettingsDialog(
                    deniedList, "蓝牙使用需要同意以下权限才能正常使用", "允许", "拒绝"
                )
            }
            .request { allGranted, grantedList, deniedList ->
                if (allGranted) {
                    if (GpsUtil.isGpsEnable(this)) {
                        if (isBluetoothEnable()) {
                            LogUtils.d("蓝牙已开启")
                            isNeedPermission = true
                            permissionCallback?.invoke()
                        } else {
                            //打开蓝牙
                            enableBluetooth()
                        }
                    } else {
                        showOpenGpsDialog()
                    }
                } else {
                    showToast(getString(R.string.aimed_common_permission))
                }
            }
    }

    /**
     * 请求打开蓝牙
     */
    private fun enableBluetooth() {
        try {
            val openIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
            startActivityForResult(openIntent, BleConstants.ACTION_ENABLE_BLUETOOTH)
        } catch (e: SecurityException) {
            // 处理异常情况,例如提示用户权限被拒绝
            LogUtils.d("请先授予蓝牙权限")
            showToast("请先授予蓝牙权限")
        }
    }

    private fun showOpenGpsDialog() {
        // 创建一个 AlertDialog.Builder 实例
        AlertDialog.Builder(this)
            .setTitle("位置信息未启用") // 设置标题
            .setMessage(StringUtils.getString(R.string.aimed_local_not_open)) // 设置内容
            .setPositiveButton(
                "前往设置"
            ) { dialog, which -> // 用户点击“确定”按钮后的逻辑

                // 注册广播接收器
                val filter = IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION)
                registerReceiver(gpsStatusReceiver, filter)

                startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
                dialog.dismiss()
            }
            .setNegativeButton(
                "取消"
            ) { dialog, which -> // 用户点击“取消”按钮后的逻辑
                dialog.dismiss()
            }
            .setCancelable(false) // 禁止用户通过点击外部关闭弹窗
            .show() // 显示弹窗
    }

    /**
     * 请求打开蓝牙回调
     * @param requestCode
     * @param resultCode
     * @param data
     */
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        LogUtils.d("onActivityResult:$requestCode")
        if (requestCode == BleConstants.ACTION_ENABLE_BLUETOOTH && resultCode == RESULT_OK) {
            isNeedPermission = true
            permissionCallback?.invoke()
        }
    }

    /**
     * 表示检查设备是否支持低功耗蓝牙(BLE)
     */
    fun isSupportBle(): Boolean {
        return this.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
    }

    /**
     * 检查设备是否支持蓝牙(包含BLE和经典蓝牙)
     */
    fun isSupportBluetooth(): Boolean {
        val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
        return bluetoothAdapter != null
    }

    /**
     * 蓝牙是否开启
     */
    fun isBluetoothEnable(): Boolean {
        val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
        return bluetoothAdapter != null && bluetoothAdapter.isEnabled
    }
    
    override fun onDestroy() {
        super.onDestroy()
        // 注销广播接收器
        unregisterReceiver(gpsStatusReceiver)
    }
}

GpsUtil .java

public class GpsUtil {
    /**
     * GPS 或网络定位服务是否启用
     */
    public static boolean isGpsEnable(Context context) {
        if (context == null) {
            return false;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            // Android 10+ 无需检查位置服务状态,只需要检查位置权限
            return true;
        } else {
            // Android 10 及以下需要检查位置服务
            LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
//            List<String> providers = locationManager.getAllProviders();
//            LogUtils.d("位置信息:" + providers.toString());

            boolean gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
            boolean network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
            boolean fused = locationManager.isProviderEnabled(LocationManager.FUSED_PROVIDER);//融合多种数据源(GPS、Wi-Fi、基站、传感器)计算位置,由 Google Play 服务提供。
            return gps || network || fused;
        }
    }
}
### 如何在 Android Studio 中正确申请蓝牙权限 为了确保应用程序能够在运行时访问设备的蓝牙功能,开发者需要遵循特定流程来请求必要的权限。以下是关于如何在 Android Studio 中正确申请蓝牙权限的相关说明。 #### 权限声明 在 `AndroidManifest.xml` 文件中,必须显式声明所需的蓝牙权限。通常情况下,至少需要以下两个基本权限: - **BLUETOOTH**: 允许程序连接到已配对的蓝牙设备并发现可检测的设备。 - **BLUETOOTH_ADMIN**: 允许程序发起设备可见性请求以及扫描其他蓝牙设备。 此外,在 Android 12 (API Level 31) 及更高版本中,还需要额外声明位置权限[^3]。 ```xml <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <!-- 针对 Android 12 或更高的 API --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> ``` #### 运行时权限处理 自 Android 6.0 (API Level 23) 开始,某些敏感权限(如蓝牙)需通过动态方式获取用户的授权。这意味着即使已在清单文件中声明了所需权限,仍可能需要进一步确认用户是否同意授予这些权限[^4]。 ##### 检查当前权限状态 可以利用 ContextCompat 类中的 checkSelfPermission 方法判断目标权限的状态。如果返回值为 PackageManager.PERMISSION_GRANTED,则表示已经获得该权限;反之则尚未得到许可。 ```java if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){ // 请求缺失的权限 } ``` ##### 提交权限请求 当检测到缺少必要权限时,应调用 requestPermissions 向用户发出提示框询问其意愿。此操作会触发 onRequestPermissionsResult 回调函数以便后续逻辑执行。 ```java ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.BLUETOOTH, Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CODE_BLUETOOTH_PERMISSION); ``` 注意:REQUEST_CODE_BLUETOOTH_PERMISSION 是一个自定义常量用于区分不同权限组请求场景。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值