一、Android 中对权限的定义
根据 Android 官方文档,Android 中的权限用于保护对受限数据和受限操作的访问/执行权限,目的是为用户隐私提供支持。
二、使用权限的工作流
Android官网向我们给出了在 Android 中使用权限的概要工作流,如图:
1. 判断能否在不声明权限的情况下提供功能
在实现某个功能之前,首先需要评估是否需要权限。某些功能可能无需权限即可实现,或者可以通过其他方式(如使用系统提供的服务或框架)来实现,而无需显式声明权限。
2. 不使用权限或在 manifest
文件中声明权限
如果功能可以在不访问受限数据或操作的情况下实现,直接实现功能,无需声明权限。否则,必须在 AndroidManifest.xml
文件中声明所需的权限。例如:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
3. 判断是否是运行时权限
a. 危险权限
危险权限涉及用户隐私或设备安全性,需要在运行时请求用户授予权限。例如 CAMERA
、READ_CONTACTS
、ACCESS_FINE_LOCATION
等。
b. 正常权限
正常权限在安装时自动授予,无需运行时请求。例如 INTERNET
、ACCESS_NETWORK_STATE
等。
4. 请求用户在运行时授予权限
如果权限属于危险权限,则需要在运行时向用户请求该权限。Android 提供了权限请求机制,通常通过以下步骤实现:
a. 检查权限
使用 ContextCompat.checkSelfPermission()
方法检查是否已授予权限。
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
// 权限未授予,需要请求权限
} else {
// 权限已授予,直接执行操作
}
b. 请求权限
如果权限未授予,使用 ActivityCompat.requestPermissions()
方法请求权限。
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CODE);
c. 处理权限请求结果
在 onRequestPermissionsResult()
方法中处理用户对权限请求的响应。
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 用户授予了权限,执行操作
} else {
// 用户拒绝了权限,处理拒绝的情况
}
}
}
三、权限类型
在工作流中,我们需要判断权限是正常权限还是危险权限。事实上,Android 官方将权限分为不同的类型,包括 安装时权限、运行时权限 和 特殊权限。每种权限类型指明了系统授予应用该权限后,应用可以访问的受限数据范围以及可以执行的受限操作范围。权限的保护级别取决于其类型,具体信息可以在 API 参考文档 中查看。
通常,我们将权限分为 正常权限 和 危险权限:
1. 正常权限
正常权限涵盖应用需要访问其沙盒外部数据或执行会影响沙盒外部数据的操作,但这些操作对用户隐私或其他应用操作几乎没有风险。这些权限在应用安装时由系统自动授予,无需用户明确同意。应用无需在运行时请求这些权限。
示例:
INTERNET
:允许应用打开网络套接字。ACCESS_NETWORK_STATE
:允许应用访问有关网络连接的信息。
2. 危险权限
危险权限涵盖应用需要访问的敏感用户数据或对设备操作有重大影响的操作。这些权限需要在运行时由应用显式请求,并需要用户明确授予权限。危险权限通常与用户隐私或设备安全性相关。
示例:
CAMERA
:允许应用使用设备的摄像头。READ_CONTACTS
:允许应用读取用户的联系人数据。ACCESS_FINE_LOCATION
:允许应用访问设备的精确位置(基于 GPS 和网络定位)。
3. 特殊权限
特殊权限通常涉及系统级的操作或对设备功能的深层访问。这些权限的请求方式与其他权限不同,通常需要用户在系统设置中手动授予。例如:
SYSTEM_ALERT_WINDOW
:允许应用使用TYPE_APPLICATION_OVERLAY
创建窗口,该窗口显示在所有其他应用的顶部。WRITE_SETTINGS
:允许应用读取或写入系统设置。
权限类型的总结
权限类型 | 授予时机 | 用户参与 | 示例 |
---|---|---|---|
正常权限 | 安装时自动授予 | 否 | INTERNET 、ACCESS_NETWORK_STATE |
危险权限 | 运行时请求并用户授予 | 是 | CAMERA 、READ_CONTACTS 、ACCESS_FINE_LOCATION |
特殊权限 | 用户在系统设置中授予 | 是 | SYSTEM_ALERT_WINDOW 、WRITE_SETTINGS |
通过区分不同的权限类型,Android 系统能够更好地保护用户隐私和设备安全,同时确保应用在合适的范围内访问系统资源。
四、不同版本权限的区别
Android 不同版本对于权限的管理有所变化,主要体现在权限的分类和请求方式上。此外,手机厂商对 Android 的二次开发和新兴操作系统(如鸿蒙系统)也带来了新的权限管理方式。以下是关键版本的变化,以及厂商二次开发和新兴操作系统的影响:
1. Android 6.0 (API 级别 23) 之前
在 Android 6.0 之前,权限管理相对简单但不够灵活:
- 安装时权限:应用在安装时一次性请求所有权限,用户必须统一授予所有声明的权限。
- 权限分类:权限分为普通权限和危险权限,但两者都在安装时自动授予。
- 用户控制:用户无法在应用安装后撤销权限,权限管理较为被动。
2. Android 6.0 (API 级别 23) 引入运行时权限
Android 6.0 引入了运行时权限机制,极大地增强了权限管理的灵活性和用户控制:
- 运行时权限:危险权限(如位置、摄像头、联系人等)需要在运行时动态请求,用户可以选择拒绝或授予权限。
- 权限组:危险权限被分组,请求一个权限可能隐式请求同一组的其他权限。
- 用户控制增强:用户可以在应用运行时随时撤销权限,应用需要处理权限被拒绝的情况。
开发者的变化:
- 需要编写代码检查权限状态,并在需要时请求权限。
- 处理用户拒绝权限的场景,提供替代方案或引导用户手动授权。
3. Android 10 (API 级别 29) 的权限变化
Android 10 进一步细化了权限管理,特别是在用户隐私保护方面:
- 位置权限细化:
- 新增“仅在使用时允许”选项,允许用户在应用处于后台时不授予位置权限。
- 外部存储权限限制:
- 应用只能访问自己的存储空间,除非显式请求
READ_EXTERNAL_STORAGE
或WRITE_EXTERNAL_STORAGE
权限。 - 引入了更细粒度的媒体文件访问权限,应用可以请求访问特定类型的文件(如图片、视频、音频)。
- 应用只能访问自己的存储空间,除非显式请求
4. Android 11 (API 级别 30) 的权限变化
Android 11 继续强化权限管理和用户隐私保护:
- 分区存储:
- 强制实施“分区存储”模型,限制应用对设备外部存储的访问。
- 应用只能访问自己的目录和特定类型的媒体文件,避免应用滥用存储权限。
- 一次性权限:
- 位置、麦克风和摄像头权限可以被设置为“一次性权限”,应用只能在本次会话中访问相关数据。
- 下次启动时,需要重新请求权限。
- 权限自动重置:
- 如果用户长时间未使用某个应用,系统会自动撤销其危险权限,应用需要重新请求权限。
5. Android 13 (API 级别 33) 的权限变化
Android 13 进一步优化了权限管理和用户体验:
- 通知权限:
- 通知权限被归类为危险权限,应用需要显式请求用户授权才能发送通知。
- 媒体文件权限优化:
- 引入更细粒度的媒体文件访问权限,允许应用按需请求访问特定类型的文件,如图片、视频或音频。
- 蓝牙权限调整:
- 蓝牙权限被细分为扫描、连接和广播等子权限,提高权限管理的粒度。
6. 手机厂商的二次开发
由于 Android 的开放性,各大手机厂商(如华为、小米、三星等)对原生 Android 进行了深度定制,带来了以下影响:
- 权限接口的差异:
- 厂商可能提供自定义的权限管理接口,开发者在不同设备上需要适配不同的权限请求方式。
- 权限控制的增强:
- 厂商系统可能提供更严格的权限管理,例如默认拒绝某些权限、增强的权限撤销机制等。
- 第三方 SDK 依赖:
- 某些厂商可能要求开发者通过其提供的 SDK 和证书获取特定权限,例如访问设备传感器、系统级服务等。
7. 新兴操作系统的影响
随着新兴操作系统(如鸿蒙系统)的兴起,权限管理的方式也发生了变化:
- 跨设备权限管理:
- 鸿蒙系统等新兴操作系统可能支持跨设备权限共享,允许应用在多个设备上访问用户数据或执行操作。
- 权限接口的变化:
- 这些系统可能提供全新的权限管理模型,开发者需要学习新的权限请求和处理方式。
- 安全性增强:
- 新兴系统通常会提供更强大的安全机制,例如硬件级权限保护、数据加密等。
五、开发中对不同设备的适配
在实际开发中,面对不同设备和厂商的权限接口差异,开发者通常需要采取灵活的适配策略,以确保应用在各种设备上都能正常运行。以下是一些常见的适配方法和策略:
1. 定义统一的接口
为了应对不同设备和厂商的权限管理差异,开发者可以定义一个统一的接口(例如 IMDM 接口),该接口定义了各种功能权限的标准方法和行为。然后,针对不同的设备和厂商,实现相应的具体实现类,以适配各自的权限获取方式。
示例代码结构:
// 统一接口
public interface IMDM {
void requestPermission(String permission);
boolean isPermissionGranted(String permission);
// 其他权限相关方法
}
// 具体实现类
public class SamsungMDM implements IMDM {
@Override
public void requestPermission(String permission) {
// 三星设备的权限请求实现
}
@Override
public boolean isPermissionGranted(String permission) {
// 检查三星设备的权限状态
return false;
}
}
public class HuaweiMDM implements IMDM {
@Override
public void requestPermission(String permission) {
// 华为设备的权限请求实现
}
@Override
public boolean isPermissionGranted(String permission) {
// 检查华为设备的权限状态
return false;
}
}
2. 动态获取设备信息
在运行时,通过获取设备的系统信息(如设备品牌、型号、系统版本等),确定当前设备的类型,并根据设备类型选择相应的 IMDM 实现类。
示例代码:
public class MDMFactory {
public static IMDM getMDMImplementation() {
String manufacturer = Build.MANUFACTURER.toLowerCase();
if (manufacturer.contains("samsung")) {
return new SamsungMDM();
} else if (manufacturer.contains("huawei")) {
return new HuaweiMDM();
} else {
// 默认实现,适用于其他设备
return new DefaultMDM();
}
}
}
3. 使用抽象工厂模式
通过抽象工厂模式,可以更灵活地管理不同设备的 IMDM 实现类。工厂类根据设备信息返回相应的 IMDM 实例,从而实现动态适配。
示例代码:
public class MDMFactory {
public static IMDM getMDM() {
String manufacturer = Build.MANUFACTURER.toLowerCase();
if (manufacturer.contains("samsung")) {
return new SamsungMDM();
} else if (manufacturer.contains("huawei")) {
return new HuaweiMDM();
} else {
return new DefaultMDM();
}
}
}
// 使用示例
public class App {
public static void main(String[] args) {
IMDM mdm = MDMFactory.getMDM();
mdm.requestPermission("LOCATION");
}
}
4. 考虑系统版本差异
除了设备厂商的差异,不同 Android 版本的权限管理也有所不同。因此,在适配时需要同时考虑设备厂商和系统版本。
示例代码:
public class MDMFactory {
public static IMDM getMDMImplementation() {
String manufacturer = Build.MANUFACTURER.toLowerCase();
int sdkVersion = Build.VERSION.SDK_INT;
if (manufacturer.contains("samsung")) {
return new SamsungMDM(sdkVersion);
} else if (manufacturer.contains("huawei")) {
return new HuaweiMDM(sdkVersion);
} else {
return new DefaultMDM(sdkVersion);
}
}
}
5. 处理厂商特定的权限证书
某些厂商可能要求应用使用特定的 SDK 或证书来获取某些权限。在这种情况下,开发者需要集成相应的 SDK,并在应用中进行配置。
示例:
- 华为:使用 HMS Core SDK 来获取特定权限。
- 三星:使用 Samsung SDK 来访问特定功能。
开发步骤:
-
集成厂商 SDK:
- 下载并集成厂商提供的 SDK。
- 配置 SDK 所需的证书和权限。
-
调用厂商 SDK 的权限接口:
- 在 IMDM 实现类中,调用厂商 SDK 的权限管理方法。
示例代码:
public class HuaweiMDM implements IMDM {
private final HuaweiPermissionManager huaweiPermissionManager;
public HuaweiMDM() {
huaweiPermissionManager = new HuaweiPermissionManager();
}
@Override
public void requestPermission(String permission) {
huaweiPermissionManager.requestPermission(permission);
}
@Override
public boolean isPermissionGranted(String permission) {
return huaweiPermissionManager.isPermissionGranted(permission);
}
}
总结
在 Android 开发中,权限管理是一个重要且复杂的话题,因为不同设备和厂商可能有不同的权限管理策略和接口。为了确保应用的兼容性和用户体验,开发者需要根据各个厂商的开发文档来适配不同的权限管理方案。以下是一些常见的 Android 及厂商的开发文档路径,供参考:
1. Android 官方文档
- 开发者官网:developer.android.com
- 权限管理指南:Permissions Overview
- 运行时权限:Requesting Permissions at Run Time
2. 华为 HMS Core
- 开发者官网:developer.huawei.com
- HMS Core SDK:HMS Core Services
- 权限管理:App Permissions and User Privacy
3. 小米 MIUI
- 开发者官网:dev.mi.com
- 权限管理:MIUI Permission Management
- MIUI 特性:MIUI Features
4. OPPO
- 开发者官网:open.oppomobile.com
- 权限管理:OPPO Permission Management
- ColorOS 特性:ColorOS Features
5. Vivo
- 开发者官网:open.vivo.com
- 权限管理:Vivo Permission Management
- FuntouchOS 特性:FuntouchOS Features
6. 三星 Samsung
- 开发者官网:developer.samsung.com
- 权限管理:Samsung Permission Management
- Samsung Galaxy 特性:Galaxy Features
7. 魅族 Flyme
- 开发者官网:flyme.cn
- 权限管理:Flyme Permission Management
- Flyme 特性:Flyme Features
8. 一加 OnePlus
- 开发者官网:oneplus.com
- 权限管理:一加通常遵循 Android 标准,可参考 Android 官方文档。
9. 中兴 ZTE
- 开发者官网:developer.zte.com.cn
- 权限管理:中兴通常遵循 Android 标准,可参考 Android 官方文档。
10. 荣耀 Honor
- 开发者官网:developer.hihonor.com
- 权限管理:荣耀可能参考华为 HMS Core 的权限管理,可参考华为 HMS Core 的文档。