Android 蓝牙权限申请适配全机型优化指南

前言
蓝牙技术在现代移动应用程序中的应用越来越广泛,从连接外部设备到数据传输,都离不开蓝牙功能。在 Android 平台上,为了使用蓝牙,应用程序需要获得相应的蓝牙权限。然而,由于不同的 Android 设备和版本可能存在一些差异,正确而优雅地申请蓝牙权限成为确保应用程序兼容性的关键。本文将为你提供一个优秀的指南,以确保在各种 Android 设备上成功申请蓝牙权限。

一、了解蓝牙权限

在开始之前,首先需要了解 Android 中与蓝牙相关的权限。在清单文件(AndroidManifest.xml)中添加以下权限:

    <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" tools:node="replace" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" tools:node="replace" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

BLUETOOTH 权限用于启用蓝牙功能
BLUETOOTH_ADMIN 权限则用于执行蓝牙操作,如发现设备、建立连接等。
BLUETOOTH_SCAN、BLUETOOTH_ADVERTISE 和 BLUETOOTH_CONNECT 权限,这些权限通常与蓝牙扫描、广播和连接相关。
添加了位置权限 ACCESS_FINE_LOCATION、ACCESS_COARSE_LOCATION 和 ACCESS_BACKGROUND_LOCATION,这些权限在进行蓝牙操作时可能需要,特别是在 Android 12 中更加强调了对位置权限的访问。
BLUETOOTH 和 BLUETOOTH_ADMIN 权限的声明使用 android:maxSdkVersion=“30”,这表明这两个权限只在 Android 30 版本及以下有效,这是因为 Android 31 已经移除了这两个权限的声明要求。必须添加android:maxSdkVersion=“30”,否在在其他机型会出现java.lang.SecurityException: Need BLUETOOTH permission: Neither user 10267 nor current process has android.permission.BLUETOOTH.。使用tools:node="replace"是为了覆盖第三方SDK中的蓝牙权限,避免第三方SDK未添加android:maxSdkVersion=“30”。

二、动态请求蓝牙权限

虽然在清单文件中声明权限是必要的,但从 Android 6.0(API 级别 23)开始,系统引入了运行时权限,包括蓝牙权限。因此,需要在运行时动态请求蓝牙权限。
在代码中,我们可以通过以下方式动态请求蓝牙权限:

public class PermissionUtils {
    
    public static boolean hasPermission(Context context, String permission) {
        return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED;
    }

    public static void requestPermission(Activity activity, String[] permission, int requestCode) {
        ActivityCompat.requestPermissions(activity, permission, requestCode);
    }
}

在进行蓝牙操作之前,我们需要检查并请求蓝牙权限。以下是一个示例:

public void searchBluetoothDevice() {
    // 检查权限状态
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        boolean scanPermission = PermissionUtils.hasPermission(this, Manifest.permission.BLUETOOTH_SCAN);
        boolean advertisePermission = PermissionUtils.hasPermission(this, Manifest.permission.BLUETOOTH_ADVERTISE);
        boolean connectPermission = PermissionUtils.hasPermission(this, Manifest.permission.BLUETOOTH_CONNECT);

        if (!scanPermission || !advertisePermission || !connectPermission) {
            // 有一个或多个权限未授予,需要申请权限
            PermissionUtils.requestPermission(this, new String[]{
                    Manifest.permission.BLUETOOTH_SCAN,
                    Manifest.permission.BLUETOOTH_ADVERTISE,
                    Manifest.permission.BLUETOOTH_CONNECT,
            }, 1001);
        } else {
            // 权限已授予,跳转到蓝牙页面
            navigateToBluetoothPage();
        }
    } else {
        // 处理 Android 12 之前的版本
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            checkLocationPermissions();
        } else {
            // Android 版本低于 M,直接跳转到蓝牙页面
            navigateToBluetoothPage();
        }
    }
}

public void checkLocationPermissions() {
    // 检查权限状态
    boolean locationPermission = PermissionUtils.hasPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION);
    boolean accessPermission = PermissionUtils.hasPermission(getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION);

    if (!locationPermission || !accessPermission) {
        // 有一个或多个权限未授予,需要申请权限
        PermissionUtils.requestPermission(this, new String[]{
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION,
        }, 1002);
    } else {
        // 权限已授予,跳转到蓝牙页面
        navigateToBluetoothPage();
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (requestCode == 1001) {
        // 处理蓝牙权限请求结果
        if (PermissionUtils.verifyPermissions(grantResults)) {
            // 权限已授予,跳转到蓝牙页面
            navigateToBluetoothPage();
        } else {
            // 用户拒绝了权限请求,可以进行相应的处理
        }
    } else if (requestCode == 1002) {
        // 处理位置权限请求结果
        if (PermissionUtils.verifyPermissions(grantResults)) {
            // 权限已授予,跳转到蓝牙页面
            navigateToBluetoothPage();
        } else {
            // 用户拒绝了权限请求,可以进行相应的处理
        }
    }
}

private void navigateToBluetoothPage() {
    Intent intent = new Intent(this, BluetoothPageActivity.class);
    startActivity(intent);
    finish();
}

在蓝牙搜索设备之前需要开启定位权限,代码如下:

public void checkPermissions() {
    // 检查权限状态
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        boolean locationPermission = PermissionUtils.hasPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION);
        boolean accessPermission = PermissionUtils.hasPermission(getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION);

        if (!locationPermission || !accessPermission) {
            // 有一个或多个权限未授予,需要申请权限
            PermissionUtils.requestPermission(BluetoothPageActivity.this, new String[]{
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION,
            }, 1001);
        } else {
            // 定位权限已授予,初始化蓝牙
            initBle();
        }
    } else {
        // 对于 Android 版本低于 M,直接初始化蓝牙
        initBle();
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (requestCode == 1001) {
        // 处理定位权限请求结果
        if (PermissionUtils.verifyPermissions(grantResults)) {
            // 定位权限已授予,初始化蓝牙
            initBle();
        } else {
            // 用户拒绝了权限请求,可以进行相应的处理
        }
    }
}

private void initBle() {
    // 在这里进行初始化蓝牙的操作
    // ...
}

记得在 initBle() 方法中添加蓝牙初始化的逻辑,确保在权限已授予的情况下顺利进行蓝牙搜索设备的操作。

三、兼容性优化

3.1 检查蓝牙硬件支持
在动态请求蓝牙权限之前,最好先检查设备是否支持蓝牙硬件。这可以通过以下方式实现:

private boolean isBluetoothHardwareSupported() {
    return getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
}

3.2 处理蓝牙不可用的情况
如果设备不支持蓝牙,最好显示一个提示对话框,而不是尝试启用蓝牙。

private void showBluetoothNotSupportedDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage("该设备不支持蓝牙功能")
            .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    // 关闭应用或其他操作
                }
            });
    builder.create().show();
}

3.3 显示权限被拒绝的提示
如果用户拒绝了蓝牙权限,你可以显示一个对话框,提醒用户为什么需要这个权限,并引导用户到应用程序设置中手动授予权限。

private void showPermissionDeniedDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage("蓝牙权限被拒绝,请在应用设置中手动授予权限")
            .setPositiveButton("去设置", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    openAppSettings();
                }
            })
            .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    // 关闭应用或其他操作
                }
            });
    builder.create().show();
}

// 打开应用程序设置页面
private void openAppSettings() {
    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
            Uri.parse("package:" + getPackageName()));
    startActivity(intent);
}

总结
通过上述优化,你可以更好地适配各种 Android 设备,并以用户友好的方式处理蓝牙权限的申请。这个指南提供了一个完整的解决方案,确保在不同的设备上成功使用蓝牙功能,提升应用的用户体验。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
Android机型适配是指将应用程序适配到不同的Android设备上,以确保应用在各种手机和平板电脑上正常运行。适配过程包括处理不同的屏幕尺寸、分辨率、像素密度、操作系统版本和设备功能的差异。 为了实现Android机型适配,可以采取以下几个步骤: 1. 使用布局文件:使用XML布局文件来定义应用程序的用户界面,这样可以根据屏幕尺寸和方向自动调整布局。 2. 使用尺寸单位:在布局文件中使用dp或sp等相对单位,而不是像素单位。这样可以确保界面元素在不同密度的屏幕上以相似的比例进行缩放。 3. 处理屏幕尺寸和方向变化:通过重写Activity的onConfigurationChanged()方法,可以在屏幕旋转或尺寸变化时重新布局并适应新的屏幕条件。 4. 适配资源文件:为不同的设备提供不同的资源文件,如图片、图标和字符串等。可以使用不同的文件夹命名规则(如drawable-hdpi、drawable-mdpi等)来存放不同密度的资源文件。 5. 使用功能检测:如果应用使用了某些特定功能(如摄像头、GPS等),可以使用Android提供的API进行功能检测,并在设备不支持时提供适当的替代方案。 总之,Android机型适配是一个综合考虑屏幕尺寸、分辨率、像素密度、操作系统版本和设备功能等因素的过程,通过合理的布局和资源文件管理,确保应用在各种设备上都能良好运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Android程序Su

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值