欢迎来到 Android 运行时权限示例。随着介绍安卓6.0棉花糖,Google 改变了应用程序处理权限的方式。在本教程中,我们将研究引入的新 Android 运行时权限以及如何处理它们。如果处理不当,可能会导致应用程序崩溃。
随着 Android 6.0 (SDK 23) 的推出,当用户需要使用时,系统会在运行时提示用户输入某些特定权限。因此,我们想到的第一个问题是 - 旧的应用程序会在 Android Marshmallow 上运行吗?答案是yes如果 targetSdkVersion 为 22 或更低。因此android运行时权限支持向后兼容。现在,这并不意味着我们可以通过将 sdk 版本设置为 22 来使用旧的权限模型。使用 Marshmallow 的用户可以从“设置”->“应用程序”撤销危险权限(我们稍后将讨论危险权限和正常权限) ->权限。如果我们尝试调用某个需要用户尚未授予权限的函数,该函数会突然抛出异常(java.lang.SecurityException
)这将导致应用程序崩溃。因此,我们需要在我们的应用程序中实现这个新的 Android 权限模型。
Android 将某些权限定义为危险权限,将某些权限定义为正常权限。这两种类型的共同点是它们需要在 Manifest 文件中定义。从 Android 6.0 开始,运行时仅检查危险权限,而不会检查正常权限。正常权限的一个例子是android.permission.INTERNET
。危险权限分为几类,使用户更容易了解他们允许应用程序执行哪些操作。如果用户接受组/类别中的一项权限,则他们接受整个组。危险许可的一个例子是android.permission.FINE_LOCATION
and android.permission.COARSE_LOCATION
。启用任何位置权限都会启用所有位置权限。
方法requestPermissions(String[] permissions, int requestCode);
是一个公共方法,用于请求危险权限。我们可以通过传递权限字符串数组来请求多个危险权限。Note:属于两个不同组的 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);
它返回一个整数值许可授予 or 没有权限. Note:如果用户拒绝应用程序中的关键权限,则shouldShowRequestPermissionRationale(String permission);
用于描述用户对权限的需要。让我们开发一个应用程序来检查权限是否已经存在。如果没有,则在运行时请求。
The 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>
The 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()
calls ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE);
. onRequestPermissionsResult
检查权限是否被授予。在我们的代码中,如果未授予这两种权限,则会弹出警报对话框,显示必须请求权限。要做到这一点shouldShowRequestPermissionRationale(String permission)
被调用,它会调用一个警报对话框,显示需要权限。您可以从“设置”->“应用程序”->“权限”手动撤销权限。Note:运行时权限特定方法仅自 API 23 起可用。因此,每个方法都会检查以下条件:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
The output of the android runtime permissions example application in action is given below. This brings an end to this tutorial. You can download the final Android Runtime Permissions project from the link below.
下载Android运行时权限示例项目
参考:https://developer.android.com/training/permissions/requesting.html