Android权限 - 第一篇

3773次阅读  |  发布于5年以前

本站翻译自 Android Permissions Part 1

Marshmallow提出了一套新的权限模型,在这一系列的文章中,我们将从技术上探讨如何处理权限问题,以及如何让用户体验更友好。

首先,我们需要将应用所需要的权限进行分类:核心权限与增强功能权限,核心权限是指,应用核心功能所需要的权限,一旦这些权限获取不到,将无法正常工作,而增强功能权限是指,应用拿不到这个权限时,部分周边功能无法使用,使摄像头权限来说,对于一个相机类应用来说法,拿不到摄像头权限,就无法拍照,这个应用也就失去了意义。然而,其它功能比如:给已拍摄的照片打上位置坐标所需要的ACCESS_FINE_LOCATION就不是必需的。

接下来的内容,我们以RECORD_AUDIOMODIFY_AUDIO_SETTINGS权限来举例,为了能获取这2个权限,我们需要先在Manifest中做如下声明:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.stylingandroid.permissions">

    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

    <application
        android:allowBackup="false"
        android:fullBackupContent="false"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme.NoActionBar"
        tools:ignore="GoogleAppIndexingWarning">

        <activity android:name=".MainActivity" />

        <activity
            android:name=".PermissionsActivity"
            android:label="@string/title_activity_permissions"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

这是自我们从Android API 1以来,获取权限的标准方式。当我们把targetSdkVersion指定为23或更大时,我们除了在Manifest中声明时,我们还需要在运行的时候申请。很多开发者,因为忘了在运行时申请,同时简单的把自己的targetSDKVersion指定为最高版本,从而导致了大量的Crash。因此,当我们把应用的targetSdkVersion升级时,我们首先要考虑的就是,关于权限的问题。

另个需要说明的是,现在有很多代码库用于简化我们在运行时期间的权限获取,这些库挺有用的,但我觉得我们需要真正弄明白这些库背后的处理流程,以及用户的选择。否则,使用这些库,将反过来给我们的应用带来问题。这是这一系列文章的主要目的。

Android系统将权限分为2大类,一类是危险的权限,比如RECORD_AUDIO,一类是普通的权限,比如MODIFY_AUDIO_SETTINGS。危险的权限是指会导致安全或隐私问题的权限,而普通的权限一般只是让应用获取应用域以外的数据,可能没有隐私问题或只是轻微的隐私问题。对于普通的权限,我们在Manifest中声明就好,而对于危险的权限,除了在Manifest中声明以外,还需要在运行时申请。

在应用中,我们首先需要确认的,就是我们是否已经获取了我们所需要的权限。在API 23及其以上的版本中,Context新增了一些方法用于处理权限相关的事情,当然,用ContextCompat来替代Context也是可以的。示例代码如下所示:

class PermissionsChecker {
    private final Context context;

    public PermissionsChecker(Context context) {
        this.context = context;
    }

    public boolean lacksPermissions(String... permissions) {
        for (String permission : permissions) {
        if (lacksPermission(permission)) {
            return true;
            }
        }
        return false;
    }

    private boolean lacksPermission(String permission) {
        return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_DENIED;
    }

}

对于方法ContextCompat#checkSelfPermission来说,他要么返回PackageManager.PERMISSION_DENIEDPackageManager.PERMISSION_GRANTED。在Marshmallow之前的Android版本,并不支持新的权限模型,checkSelfPermission()的逻辑为直接返回PackageManager.PERMISSION_GRANTED(如果有在Manifest中定义的话)。

接下来我们编写一个MainActivity类,在Activity中检查权限。并测试我们之前的代码。

public class MainActivity extends AppCompatActivity {
    private static final String[] PERMISSIONS = new String[] {Manifest.permission.RECORD_AUDIO, Manifest.permission.MODIFY_AUDIO_SETTINGS};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        PermissionsChecker checker = new PermissionsChecker(this);

        if (checker.lacksPermissions(PERMISSIONS)) {
            Snackbar.make(toolbar, R.string.no_permissions, Snackbar.LENGTH_INDEFINITE).show();
        }
    .
    .
    .
    }
}

Marshmallow之前的版本中,运行效果如下图所示:

Part1-lollipop.png

Marshmallow及其以后的版本中,如果我们没有给定权限,则运行结果如下图所示:

Part1-marshmallow

Copyright© 2013-2019

京ICP备2023019179号-2