Android权限模型:从分级到实战

权限,是Android安全的第一道防线。

我刚开始做Android开发时,总觉得权限就是个弹窗,用户点同意就完事了。直到有一次,我负责的App因为权限声明不当,被Google Play直接下架。嗯,从那以后,我才真正开始认真研究这套权限体系。

权限分级:Normal、Dangerous、Signature

Android把权限分成了三个等级。说白了,就是根据风险大小,决定系统怎么处理。

等级 特点 典型权限 安装时行为
Normal 低风险,不涉及用户隐私 INTERNET、ACCESS_NETWORK_STATE 自动授予,无需用户确认
Dangerous 高风险,涉及用户隐私 CAMERA、READ_CONTACTS、ACCESS_FINE_LOCATION 需要运行时弹窗申请
Signature 系统级,仅同签名应用可获取 BIND_ACCESSIBILITY_SERVICE、INSTALL_PACKAGES 系统自动授予,但需同签名

你可能会问:为什么要有Signature级别?

我举个例子。我在做系统级应用开发时,需要调用一个接口来管理WiFi热点。这个接口被保护在Signature权限下。普通App根本拿不到,只有系统签名或者和我同签名的App才能调用。这就防止了恶意应用滥用系统能力。

关键点:Normal权限在Android 6.0之后依然是自动授予的。很多新手以为所有权限都要弹窗申请,其实不是。只有Dangerous级别的才需要运行时处理。

运行时权限申请的最佳实践

运行时权限,是Android 6.0引入的机制。它的核心思想是:在需要的时候才申请,而不是安装时一股脑全要

我见过太多App一启动就弹五六个权限申请框。用户烦不烦?肯定烦。而且Google Play现在对这种做法会直接打回。

最佳实践一:按需申请

不要提前申请权限。等用户真正要用到某个功能时,再弹窗。

// 错误示范:一启动就申请所有权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
        != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.CAMERA,
                    Manifest.permission.READ_CONTACTS,
                    Manifest.permission.ACCESS_FINE_LOCATION},
            REQUEST_CODE_ALL);
}

// 正确做法:用到相机时才申请
public void openCamera() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
            != PackageManager.PERMISSION_GRANTED) {
        // 先解释为什么需要这个权限
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.CAMERA)) {
            showRationaleDialog("我们需要相机权限来拍照");
        }
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.CAMERA},
                REQUEST_CODE_CAMERA);
    } else {
        // 权限已授予,直接打开相机
        startCamera();
    }
}

我的习惯:每次申请权限前,先调用shouldShowRequestPermissionRationale()。如果返回true,说明用户之前拒绝过。这时候我会弹一个解释对话框,告诉用户为什么需要这个权限。用户体验会好很多。

最佳实践二:处理拒绝和"不再询问"

用户可能会拒绝权限,甚至勾选"不再询问"。这时候怎么办?

我曾经遇到一个坑:用户拒绝了位置权限,然后App直接崩溃了。原因是代码里没做空指针判断。从那以后,我养成了一个习惯——所有依赖权限的功能,都要做降级处理

@Override
public void onRequestPermissionsResult(int requestCode,
        @NonNull String[] permissions,
        @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_CODE_CAMERA) {
        if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 权限授予,继续
            startCamera();
        } else {
            // 权限被拒绝
            if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.CAMERA)) {
                // 用户勾选了"不再询问"
                showSettingsDialog("请在设置中手动开启相机权限");
            } else {
                // 用户只是这次拒绝
                showToast("需要相机权限才能拍照");
            }
        }
    }
}

注意:如果用户勾选了"不再询问",你再次调用requestPermissions()是不会弹窗的。系统会直接回调拒绝。这时候只能引导用户去设置页面手动开启。

最佳实践三:权限组的概念

Android把权限分成了组。比如STORAGE组包含READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE。如果你申请了读权限,用户同意了,那么写权限也会自动被授予。

但要注意:不同Android版本的分组可能不一样。我建议还是每个权限单独申请,不要依赖分组行为。

自定义权限的声明与使用

有时候,你的App需要暴露一些接口给其他App调用。这时候就需要自定义权限了。

声明自定义权限

AndroidManifest.xml中声明:

<permission
    android:name="com.example.myapp.permission.MY_CUSTOM_PERMISSION"
    android:label="我的自定义权限"
    android:description="允许访问我的App的某些功能"
    android:protectionLevel="dangerous" />

protectionLevel可以设置为normaldangeroussignature等。我一般用dangerous,因为这样其他App调用时,系统会弹窗让用户确认。

使用自定义权限保护组件

声明好权限后,就可以用它来保护你的Activity、Service、ContentProvider等组件了。

<activity
    android:name=".ProtectedActivity"
    android:permission="com.example.myapp.permission.MY_CUSTOM_PERMISSION">
    <intent-filter>
        <action android:name="com.example.myapp.action.PROTECTED_ACTION" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

其他App想启动这个Activity,必须先声明并使用这个权限:

<uses-permission android:name="com.example.myapp.permission.MY_CUSTOM_PERMISSION" />

避坑指南:我曾经犯过一个错误——自定义权限的protectionLevel设置成了signature,结果第三方App死活调不了我的接口。后来才发现,只有和我同签名的App才能获取这个权限。所以,如果你希望其他App也能调用,用dangerous级别

知识体系总览

下面这张图,是我梳理的Android权限模型核心脉络。你可以对照着看,思路会更清晰。

Android权限模型知识体系 Normal 权限 Dangerous 权限 Signature 权限 自动授予,无需弹窗 运行时弹窗申请 同签名自动授予 运行时权限申请最佳实践 按需申请 用到时才弹窗 处理拒绝 降级 + 引导设置 权限组理解 但不要依赖分组 自定义权限:声明 → 保护组件 → 其他App调用

这张图把权限分级、运行时申请、自定义权限串在了一起。你可以看到,Normal和Signature权限基本不需要开发者操心运行时逻辑,而Dangerous权限才是我们日常开发中需要重点处理的。

至于自定义权限,它更像是一种"协议"——你声明,别人遵守。我在做SDK开发时经常用到,用来保护一些内部接口不被随意调用。


权限这块,说难不难,说简单也不简单。关键是养成好习惯:按需申请、优雅降级、合理声明。做到这三点,你的App在权限安全方面基本就及格了。