Android系统 权限组管理和兼容性

简介: Android系统 权限组管理和兼容性

Android系统 文件访问权限笔记

Android系统 理解/sys/目录权限和UID和GID?

Android系统 应用存储路径与权限

Android系统 自定义系统和应用权限

Android系统 AppOps默认授予应用相应的权限

Android系统 权限组管理和兼容性


参考学习: Android 中的权限

Android权限的目的是为了保护用户数据和设备。Android系统中有两种类型的权限普通权限和危险权限 , 为了方便用户管理危险权限又分为不同的权限组 从Android 6.0开始,又引入了运行时权限机制。除了普通权限和危险权限之外 存在系统级别或签名级别的特殊权限。反正不同Android版本间的权限都有差异 越新版本权限越严格(S/B)。

核心点 详细描述
权限目的 保护用户的数据和设备安全。
权限类型 - 普通权限: 不会对用户隐私或设备造成大的影响。如访问网络。
- 危险权限: 可能对用户隐私或设备造成大的影响。如读取联系人。
权限组 危险权限被分为不同的权限组,如电话、存储、位置等。授予组内任一权限会自动授予该组其他权限。
运行时权限 从Android 6.0开始,应用在使用危险权限时需在运行时请求用户授权,而非安装时。
特殊权限 系统级别或签名级别的权限,如修改系统设置。通常只由系统应用或相同签名的应用使用,但有例外情况。
版本差异 不同Android版本间的权限有差异,比如从6.0引入运行时权限,10.0开始限制后台位置访问。

权限组

权限组的概念和作用

权限组是一种将相关的危险权限进行分类和分组的方式,每个危险权限都属于一个或多个权限组,如存储、相机等。权限组的作用是方便用户管理危险权限,减少用户频繁地处理授权请求,也让用户更清楚地知道应用需要什么样的功能。

Android系统中有以下几个权限组:

  • 存储:允许应用访问外部存储空间,如读取和写入文件、获取存储信息等。
  • 相机:允许应用使用设备的相机功能,如拍照、录像、获取相机参数等。

每个权限组中包含了一个或多个危险权限,可以在这里查看每个权限组中具体包含哪些危险权限。

如何定义和使用权限组

在开发应用时,需要在AndroidManifest.xml文件中声明需要的权限,无论是普通权限还是危险权限。可以使用<uses-permission>标签来声明一个单独的权限,也可以使用<uses-permission-group>标签来声明一个整个的权限组。例如:

<!-- 声明单独的危险权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 声明整个的权限组 -->
<uses-permission-group android:name="android.permission-group.CAMERA" />
<uses-permission-group android:name="android.permission-group.MICROPHONE" />

如果声明了一个整个的权限组,那么不需要再声明该组中的单独的危险权限了。但是如果只声明了该组中的某些危险权限,那么就只能使用这些声明过的危险权限,而不能使用该组中其他没有声明过的危险权限。例如:

<!-- 声明存储组中的写入外部存储危险权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 不能使用存储组中其他没有声明过的危险权限,如读取外部存储 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

在运行时,需要向用户请求授权需要的危险权限,可以使用ActivityCompat.requestPermissions()方法来请求一个或多个危险权限,也可以使用ActivityCompat.requestPermissionsFromGroup()方法来请求一个整个的权限组。例如:

// 请求单独的危险权限
ActivityCompat.requestPermissions(this,
        new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO},
        REQUEST_CODE);
// 请求整个的权限组
ActivityCompat.requestPermissionsFromGroup(this,
        Manifest.permission-group.CAMERA,
        REQUEST_CODE);

如果请求了一个整个的权限组,那么用户只需要一次授权就可以授予该组中的所有危险权限。但是如果只请求了该组中的某些危险权限,那么用户只会授予这些请求过的危险权限,而不会授予该组中其他没有请求过的危险权限。例如:

// 请求存储组中的写入外部存储危险权限
ActivityCompat.requestPermissions(this,
        new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
        REQUEST_CODE);
// 用户只会授予写入外部存储危险权限,而不会授予存储组中其他没有请求过的危险权限,如读取外部存储

在请求权限时,需要提供一个请求码,用于标识请求。在用户做出授权选择后,可以在onRequestPermissionsResult()方法中接收用户的选择结果,并根据结果进行相应的处理。例如:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    // 判断请求码是否匹配
    if (requestCode == REQUEST_CODE) {
        // 判断授权结果是否成功
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 授权成功,执行相应的操作
            Toast.makeText(this, "Permission granted", Toast.LENGTH_SHORT).show();
        } else {
            // 授权失败,提示用户或者退出应用
            Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();
        }
    }
}

在使用危险权限时,需要先检查是否已经获得了该权限,可以使用ContextCompat.checkSelfPermission()方法来检查某个单独的危险权限,也可以使用ContextCompat.checkPermissionGroup()方法来检查某个整个的权限组。例如:

// 检查单独的危险权限
int cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
int audioPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO);
// 检查整个的权限组
int cameraGroup = ContextCompat.checkPermissionGroup(this, Manifest.permission-group.CAMERA);
int microphoneGroup = ContextCompat.checkPermissionGroup(this, Manifest.permission-group.MICROPHONE);

如果检查结果为PackageManager.PERMISSION_GRANTED,则表示已经获得了该权限或者该组中的所有权限,可以直接使用。如果检查结果为PackageManager.PERMISSION_DENIED,则表示没有获得了该权限或者该组中的任何一个权限,需要向用户请求授权。例如:

// 检查存储组中的写入外部存储危险权限
int writePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (writePermission == PackageManager.PERMISSION_GRANTED) {
    // 已经获得了写入外部存储危险权限,可以直接使用
    saveFileToExternalStorage();
} else {
    // 没有获得了写入外部存储危险权限,需要向用户请求授权
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
            REQUEST_CODE);
}

权限管理

Android系统的权限管理机制

Android的权限管理机制涉及应用如何请求、获得和使用权限,以及用户如何授予和管理这些权限。早期版本(6.0以下)在安装时请求权限,而6.0及以上版本在运行时请求危险权限。从11.0开始,用户可以授予应用一次性的使用权,特定于当前会话。而从10.0开始,应用要在后台访问位置信息,除了需要精确位置权限外,还需要一个特殊的后台位置访问权限。

权限管理类型 描述 适用版本
安装时权限 应用在安装时展示所需权限,用户选择是否接受。权限可以在系统设置中启用或禁用,但不能单独控制。 6.0以下
运行时权限 应用在运行时请求危险权限。用户可以在系统设置中查看或修改,可以单独或整组控制。 6.0及以上
一次性权限 用户可以授予应用当前会话的一次性使用权。权限在应用退出或设备锁屏后失效。 11.0及以上
后台位置访问 应用需要特殊的后台位置访问权限才能在后台获取位置信息,否则只能在前台访问。 10.0及以上

如何在应用中实现权限管理

在开发Android应用时,为了提高功能、安全性和用户体验,开发者应仅请求必要的权限,明确告知用户为何需要某权限,尊重用户的选择和隐私,并适配不同版本的权限特性和规则。

建议 描述
只请求必要的权限 根据应用功能和需求确定所需权限,避免请求不必要或冗余的权限,以增加用户信任。
解释权限需求 在请求危险权限前,明确告知用户为何需要该权限及其用途,以减少用户疑虑。
尊重用户选择和隐私 接受用户的授权决策,无论同意或拒绝。避免强迫、诱导或反复请求。确保不滥用或泄露用户数据。
适配版本的权限特性 考虑到Android不同版本间的权限差异,如6.0的运行时权限和10.0的后台位置访问限制,确保应用在各版本上正常运行。

以确保应用在各个版本上都能正常运行。可以使用Build.VERSION.SDK_INT来判断当前设备的系统版本,并根据版本进行相应的处理。例如:

// 判断当前设备是否为6.0及以上版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    // 是6.0及以上版本,使用运行时权限机制
    // 检查是否已经获得某个危险权限
    int cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
    if (cameraPermission == PackageManager.PERMISSION_GRANTED) {
        // 已经获得了危险权限,可以直接使用
        openCamera();
    } else {
        // 没有获得了危险权限,需要向用户请求授权
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.CAMERA},
                REQUEST_CODE);
    }
} else {
    // 是6.0以下版本,使用安装时权限机制
    // 不需要检查或请求危险权限,可以直接使用
    openCamera();
}

权限兼容性

Android不同版本间的权限差异

Android系统在不同版本中对权限有所变化。从6.0开始,引入了运行时权限机制,要求应用在运行时请求危险权限。11.0版本引入了一次性权限,允许用户为敏感权限授予一次性使用权。从10.0开始,应用在后台访问位置信息需要特殊的权限,并引入了分区存储模式,限制应用直接访问外部存储。

特性/变化 描述 适用版本
运行时权限机制 应用在运行时请求危险权限,而非安装时。 6.0及以上
一次性权限 用户可以为敏感权限(如位置、相机)授予一次性使用权,仅在当前会话有效。 11.0及以上
后台位置访问 应用在后台访问位置需要ACCESS_BACKGROUND_LOCATION权限,而非仅ACCESS_FINE_LOCATION 10.0及以上
分区存储 应用只能访问自己的沙盒存储,访问其他存储需通过特定API或权限。 10.0及以上

如何处理权限的兼容性问题

开发Android应用时,为确保在各版本正常运行,开发者需考虑权限差异。建议设置最新的目标SDK版本,根据设备系统版本判断权限需求,并使用Jetpack兼容库简化权限处理。

建议 描述 示例/备注
设置目标SDK版本 在AndroidManifest.xml中设置应用的目标SDK版本,尽量选择最新版本以适配新权限特性。 若目标SDK低于6.0,应用使用安装时权限机制,可能降低透明度和信任度。
判断当前系统版本 在请求/使用权限前,检查设备系统版本以确定权限需求。 使用Build.VERSION.SDK_INT判断。例如,10.0及以上版本需请求后台位置访问权限。
使用兼容库 利用Android Jetpack的兼容库简化不同版本间的权限处理。 使用PermissionCompat请求危险权限,无需判断版本。

可以使用Build.VERSION.SDK_INT来判断当前设备的系统版本,并根据版本进行相应的处理。例如:

// 判断当前设备是否为10.0及以上版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    // 是10.0及以上版本,需要请求后台位置访问危险权限
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
            REQUEST_CODE);
} else {
    // 是10.0以下版本,不需要请求后台位置访问危险权限
    // 可以直接使用位置信息
    getLocation();
}

可以使用Android Jetpack中提供的一些兼容库来简化和统一对于不同版本间权限差异的处理。例如,可以使用PermissionCompat类来请求一个或多个危险权限,而不需要判断当前设备是否为6.0及以上版本。例如:

// 使用PermissionCompat类来请求危险权限
PermissionCompat.requestPermissions(this,
        new String[]{Manifest.permission.CAMERA},
        REQUEST_CODE);

如何请求和使用特殊权限

在开发应用时,如果需要使用某些特殊权限来实现一些高级或者特殊的功能,需要以下几个步骤:

  • 声明特殊权限:需要在AndroidManifest.xml文件中声明需要使用的特殊权限,使用<uses-permission>标签来声明一个单独的特殊权限。例如:
<!-- 声明修改系统设置特殊权限 -->
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
  • 检查特殊权限:需要在使用某个特殊权限之前,检查是否已经获得了该特殊权限,可以使用Settings.System.canWrite()方法来检查修改系统设置特殊权限,使用Settings.canDrawOverlays()方法来检查悬浮窗特殊权限,使用DevicePolicyManager.isAdminActive()方法来检查设备管理器特殊权限,使用NotificationManager.isNotificationPolicyAccessGranted()方法来检查通知监听特殊权限,使用AccessibilityManager.isEnabled()方法来检查辅助功能特殊权限,使用PackageManager.canRequestPackageInstalls()方法来检查安装未知来源应用特殊权限。例如:
// 检查修改系统设置特殊权限
boolean canWrite = Settings.System.canWrite(this);
// 检查悬浮窗特殊权限
boolean canDraw = Settings.canDrawOverlays(this);
// 检查设备管理器特殊权限
DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
ComponentName componentName = new ComponentName(this, MyDeviceAdminReceiver.class);
boolean isAdminActive = devicePolicyManager.isAdminActive(componentName);
// 检查通知监听特殊权限
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
boolean isNotificationAccessGranted = notificationManager.isNotificationPolicyAccessGranted();
// 检查辅助功能特殊权限
AccessibilityManager accessibilityManager = (AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE);
boolean isAccessibilityEnabled = accessibilityManager.isEnabled();
// 检查安装未知来源应用特殊权限
PackageManager packageManager = getPackageManager();
boolean canRequestPackageInstalls = packageManager.canRequestPackageInstalls();

如果检查结果为true,则表示已经获得了该特殊权限,可以直接使用。如果检查结果为false,则表示没有获得了该特殊权限,需要向用户请求授权。

  • 请求特殊权限:需要在运行时向用户请求授权需要的特殊权限,以使用startActivityForResult()方法来启动一个系统提供的设置界面,让用户在该界面中授予或拒绝请求。需要提供一个请求码,用于标识请求。在用户做出授权选择后,可以在onActivityResult()方法中接收用户的选择结果,并根据结果进行相应的处理。例如:
// 请求修改系统设置特殊权限
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
// 请求悬浮窗特殊权限
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
// 请求设备管理器特殊权限
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, componentName);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "请授予设备管理器权限");
startActivityForResult(intent, REQUEST_CODE);
// 请求通知监听特殊权限
Intent intent = new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
startActivityForResult(intent, REQUEST_CODE);
// 请求辅助功能特殊权限
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivityForResult(intent, REQUEST_CODE);
// 请求安装未知来源应用特殊权限
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    // 判断请求码是否匹配
    if (requestCode == REQUEST_CODE) {
        // 判断授权结果是否成功
        if (canWrite || canDraw || isAdminActive || isNotificationAccessGranted || isAccessibilityEnabled || canRequestPackageInstalls) {
            // 授权成功,执行相应的操作
            Toast.makeText(this, "Permission granted", Toast.LENGTH_SHORT).show();
        } else {
            // 授权失败,提示用户或者退出应用
            Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();
        }
    }
}

希望这篇文章能对您有所帮助。如果还有其他问题或建议,请留言与私信。

相关文章
|
1月前
|
Android开发
Android 如何将定制的Launcher成为系统中唯一的Launcher
Android 如何将定制的Launcher成为系统中唯一的Launcher
32 2
|
1天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的安卓的微博客系统的详细设计和实现
基于SpringBoot+Vue+uniapp的安卓的微博客系统的详细设计和实现
5 0
|
2天前
|
Java Linux Android开发
Android面试题之说说系统的启动流程(总结)
这篇文章概述了Android系统的启动流程,从Boot Rom到Zygote进程和SystemServer的启动。init进程作为用户级别的第一个进程,负责创建文件目录、初始化服务并启动Zygote。Zygote通过预加载资源和创建Socket服务,使用fork函数生成SystemServer进程。fork过程中,子进程继承父进程大部分信息但具有独立的进程ID。Zygote预加载资源以减少后续进程的启动时间,而SystemServer启动众多服务并最终开启Launcher应用。文中还讨论了为何从Zygote而非init或SystemServer fork新进程的原因。
9 2
|
8天前
|
前端开发 Java API
Android系统中读写和显示图片
Android系统中读写和显示图片
8 0
|
9天前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的安卓的微博客系统附带文章和源代码部署视频讲解等
基于ssm+vue.js+uniapp小程序的安卓的微博客系统附带文章和源代码部署视频讲解等
20 2
|
11天前
|
Java 机器人 Linux
01. 【Android教程】系统背景及结构概述
01. 【Android教程】系统背景及结构概述
8 0
|
16天前
|
运维 监控 Android开发
构建高效自动化运维系统的策略与实践构建高效Android应用:Kotlin协程的实践指南
【5月更文挑战第29天】随着信息技术的迅猛发展,企业IT基础设施变得日益复杂,传统的手动运维模式已难以满足高效率、高稳定性的要求。本文将深入探讨如何通过自动化工具和策略来构建一个高效的自动化运维系统。文中不仅分析了自动化运维的必要性,还详细介绍了实现过程中的关键步骤,包括监控、配置管理、故障响应等,并结合实际案例分析其效果,以期为读者提供一套行之有效的自动化运维解决方案。
|
16天前
|
人工智能 vr&ar Android开发
安卓与iOS系统的发展趋势及影响分析
在移动互联网时代,安卓和iOS作为两大主流移动操作系统,在不断发展变化中展现出不同的特点和发展趋势。本文从技术性角度出发,分析了安卓和iOS系统的发展趋势,并探讨了它们对移动设备市场和用户体验的影响,帮助读者更好地理解当前移动操作系统的发展方向和未来可能的变化。
17 0
|
18天前
|
存储 人工智能 安全
移动应用与系统:探索开发与操作系统的融合安卓应用开发:打造高效用户界面的关键技术
【5月更文挑战第27天】 随着移动互联网的飞速发展,移动应用和操作系统已经成为了我们日常生活中不可或缺的一部分。本文将深入探讨移动应用开发的关键要素,以及移动操作系统的核心功能。我们将分析移动应用开发的挑战和机遇,并讨论移动操作系统如何适应不断变化的技术环境。通过深入研究这些主题,我们希望为读者提供对移动应用和系统领域的全面理解。
|
18天前
|
存储 缓存 算法
深入理解操作系统内存管理:分页系统的优势与挑战构建高效Android应用:探究Kotlin协程的优势与实践
【5月更文挑战第27天】 在现代计算机系统中,内存管理是操作系统的核心功能之一。分页系统作为一种内存管理技术,通过将物理内存划分为固定大小的单元——页面,为每个运行的程序提供独立的虚拟地址空间。这种机制不仅提高了内存的使用效率,还为多任务环境提供了必要的隔离性。然而,分页系统的实现也带来了一系列的挑战,包括页面置换算法的选择、内存抖动问题以及TLB(Translation Lookaside Buffer)的管理等。本文旨在探讨分页系统的原理、优势及其面临的挑战,并通过分析现有解决方案,提出可能的改进措施。