Android运行时权限示例

欢迎来到Android运行时权限示例。随着Android 6.0 Marshmallow的推出,Google改变了应用处理权限的方式。在本教程中,我们将深入了解引入的新Android运行时权限以及如何处理它们。如果处理不当,可能会导致应用程序崩溃。

什么是Android运行时权限?

随着Android 6.0(SDK 23)的推出,当使用某些权限变得必要时,用户会在运行时收到提示。因此,我们首先要考虑的问题是 – 老的应用程序是否能在Android Marshmallow上运行?答案是是的,如果targetSdkVersion小于等于22。因此,Android运行时权限支持向后兼容。现在这并不意味着我们可以通过将SDK版本设置为22来使用旧的权限模型。使用Marshmallow的用户可以从“设置”->“应用程序”->“权限”中撤销危险权限(稍后我们将讨论危险和普通权限)。在我们尝试调用某些需要用户尚未授予的权限的功能的情况下,该功能将突然抛出异常(java.lang.SecurityException),导致应用程序崩溃。因此,我们需要在我们的应用程序中实现这个新的Android权限模型。

危险和普通的安卓权限

安卓将一些权限定义为危险权限,而将一些定义为普通权限。两种类型的共同之处是它们都需要在清单文件中定义。从安卓 6.0 开始,只有危险权限在运行时进行检查,普通权限则不会。一个普通权限的示例是android.permission.INTERNET。危险权限被分组到类别中,使用户更容易理解他们正在允许应用程序执行的操作。如果用户接受了组/类别中的一个权限,则表示他们接受整个组。危险权限的一个示例是android.permission.FINE_LOCATIONandroid.permission.COARSE_LOCATION。启用任何一个位置权限都会启用全部。

请求安卓运行时权限

方法`requestPermissions(String[] permissions, int requestCode);`是一个公共方法,用于请求危险权限。我们可以通过传递权限字符串数组来请求多个危险权限。注意:Android权限属于两个不同组的话,会为每个组显示独立的对话框。如果它们属于同一组,则只会显示一个对话框提示。请求的结果将传递到方法`onRequestPermissionResult`中。例如:假设我们想在应用中访问相机和位置。这两个都是危险权限。当应用启动时,我们将显示一个提示请求访问这些权限的提示。让我们将权限添加到一个字符串数组中,并调用`requestPermissions`方法如下所示:

String[] perms = {"android.permission.FINE_LOCATION", "android.permission.CAMERA"};

int permsRequestCode = 200; 
requestPermissions(perms, permsRequestCode);

@Override
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults){

    switch(permsRequestCode){

        case 200:

            boolean locationAccepted = grantResults[0]==PackageManager.PERMISSION_GRANTED;
            boolean cameraAccepted = grantResults[1]==PackageManager.PERMISSION_GRANTED;

            break;

    }

}

现在我们不希望用户继续接受他已经接受过的权限。即使权限已经被授予,也有必要再次检查以确保用户没有后来撤销该权限。为此,需要在每个权限上调用以下方法。

checkSelfPermission(String perm);

它返回一个整数值PERMISSION_GRANTEDPERMISSION_DENIED注意:如果用户拒绝了应用中的关键权限,则使用`shouldShowRequestPermissionRationale(String permission);`来描述用户需要该权限的原因。让我们开发一个应用程序,检查权限是否已经存在。如果没有,则在运行时请求它。

Android Runtime Permissions 项目结构

Android Runtime Permissions 代码

content_main.xml 包含两个按钮,用于检查和请求权限。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.journaldev.runtimepermissions.MainActivity"
    tools:showIn="@layout/activity_main">
    <Button
        android:id="@+id/check_permission"
        android:layout_width="match_parent"
        android:layout_centerInParent="true"
        android:layout_height="wrap_content"
        android:text="Check Permission"/>
    <Button
        android:id="@+id/request_permission"
        android:layout_below="@+id/check_permission"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Request Permission"/>
</RelativeLayout>

MainActivity.java 定义如下:

package com.journaldev.runtimepermissions;

import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;

import android.view.View;
import android.widget.Button;

import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.CAMERA;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final int PERMISSION_REQUEST_CODE = 200;
    private View view;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        Button check_permission = (Button) findViewById(R.id.check_permission);
        Button request_permission = (Button) findViewById(R.id.request_permission);
        check_permission.setOnClickListener(this);
        request_permission.setOnClickListener(this);


    }


    @Override
    public void onClick(View v) {

        view = v;

        int id = v.getId();
        switch (id) {
            case R.id.check_permission:
                if (checkPermission()) {

                    Snackbar.make(view, "Permission already granted.", Snackbar.LENGTH_LONG).show();

                } else {

                    Snackbar.make(view, "Please request permission.", Snackbar.LENGTH_LONG).show();
                }
                break;
            case R.id.request_permission:
                if (!checkPermission()) {

                    requestPermission();

                } else {

                    Snackbar.make(view, "Permission already granted.", Snackbar.LENGTH_LONG).show();

                }
                break;
        }

    }

    private boolean checkPermission() {
        int result = ContextCompat.checkSelfPermission(getApplicationContext(), ACCESS_FINE_LOCATION);
        int result1 = ContextCompat.checkSelfPermission(getApplicationContext(), CAMERA);

        return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED;
    }

    private void requestPermission() {

        ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE);

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case PERMISSION_REQUEST_CODE:
                if (grantResults.length > 0) {

                    boolean locationAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
                    boolean cameraAccepted = grantResults[1] == PackageManager.PERMISSION_GRANTED;

                    if (locationAccepted && cameraAccepted)
                        Snackbar.make(view, "Permission Granted, Now you can access location data and camera.", Snackbar.LENGTH_LONG).show();
                    else {

                        Snackbar.make(view, "Permission Denied, You cannot access location data and camera.", Snackbar.LENGTH_LONG).show();

                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                            if (shouldShowRequestPermissionRationale(ACCESS_FINE_LOCATION)) {
                                showMessageOKCancel("You need to allow access to both the permissions",
                                        new DialogInterface.OnClickListener() {
                                            @Override
                                            public void onClick(DialogInterface dialog, int which) {
                                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                                    requestPermissions(new String[]{ACCESS_FINE_LOCATION, CAMERA},
                                                            PERMISSION_REQUEST_CODE);
                                                }
                                            }
                                        });
                                return;
                            }
                        }

                    }
                }


                break;
        }
    }


    private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
        new AlertDialog.Builder(MainActivity.this)
                .setMessage(message)
                .setPositiveButton("OK", okListener)
                .setNegativeButton("Cancel", null)
                .create()
                .show();
    }

}

注意:在 Manifest 文件中,在应用程序标签上方添加需要在运行时检查的权限;

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

在上述代码中,检查和请求的两个权限是相机和位置。导入静态权限完整类名允许我们仅编写`PERMISSION`对象,而不是完全限定的路径。`checkPermission()`调用了每个权限的`checkSelfPermission`。`requestPermission()`调用了`ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE);`。`onRequestPermissionsResult`检查权限是否已授予。在我们的代码中,如果两个权限都未被授予,则会弹出警报对话框,显示必须请求权限的必要性。为此,调用了`shouldShowRequestPermissionRationale(String permission)`,它调用了一个显示权限需求的警报对话框。您可以从设置->应用程序->权限手动撤销权限。注意: 运行时权限特定方法仅在API 23及以上版本可用。因此,在每个方法中都会检查以下条件:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)

Android运行时权限示例应用程序的输出如下。 这就是本教程的全部内容。您可以从下面的链接下载最终的Android运行时权限项目。

下载Android运行时权限示例项目

参考:https://developer.android.com/training/permissions/requesting.html

Source:
https://www.digitalocean.com/community/tutorials/android-runtime-permissions-example