Android Runtime 權限示例

歡迎來到Android運行時權限示例。隨著Android 6.0 Marshmallow的推出,Google改變了應用程式處理權限的方式。在本教程中,我們將研究新的Android運行時權限以及如何處理它們。如果不正確處理,可能會導致應用程式崩潰。

什麼是Android運行時權限?

在Android 6.0(SDK 23)的引入中,當使用權限變得必要時,用戶將在運行時提示授予某些特定權限。因此,我們首先要想到的問題是-舊應用程式能在Android Marshmallow上運行嗎?答案是是的,如果目標SDK版本為22或更低。因此,Android運行時權限支援向後兼容。現在,這並不意味著我們可以通過將SDK版本設置為22來使用舊的權限模型。使用Marshmallow的用戶可以從「設定」-「應用程式」-「權限」中撤銷危險權限(稍後我們將討論危險和普通權限)。在我們嘗試調用某些需要用戶尚未授予的權限的函數時,該函數將突然拋出一個異常(java.lang.SecurityException),導致應用程式崩潰。因此,我們需要在應用程式中實現這個新的Android權限模型。

危險和正常的Android權限

Android將一些權限定義為危險權限,而一些定義為正常權限。這兩種類型的共同之處在於它們需要在清單文件中定義。從Android 6.0開始,只有危險權限在運行時進行檢查,正常權限不受檢查。正常權限的一個示例是android.permission.INTERNET。危險權限被分組為類別,使用戶更容易理解他們允許應用進行的操作。如果用戶接受組/類別中的一個權限,則表示他們接受整個組。危險權限的一個示例是android.permission.FINE_LOCATIONandroid.permission.COARSE_LOCATION。啟用位置權限中的任何一個將啟用所有權限。

請求Android運行時權限

方法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" />

在上述代碼中,檢查和請求的兩個權限分別是相機(CAMERA)和位置(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