欢迎来到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_LOCATION
和android.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_GRANTED或PERMISSION_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运行时权限项目。
参考:https://developer.android.com/training/permissions/requesting.html
Source:
https://www.digitalocean.com/community/tutorials/android-runtime-permissions-example