Android 运行时权限的例子
欢迎来到Android运行时权限示例。随着Android 6.0 Marshmallow的推出,谷歌改变了应用程序处理权限的方式。在本教程中,我们将介绍新引入的Android运行时权限以及如何处理它们。如果处理不当,可能会导致应用程序崩溃。
什么是Android运行时权限?
随着Android 6.0的推出(SDK 23),当需要使用某些特定权限时,用户会在运行时收到提示。因此,我们首先要考虑的问题是 – 老版本的应用程序能在Android Marshmallow上运行吗?答案是,如果目标SDK版本为22或更低,则可以。因此,Android运行时权限支持向后兼容。现在,这并不意味着我们可以通过将SDK版本设置为22来使用旧的权限模型。Marshmallow用户可以从设置->应用程序->权限中撤销危险权限(稍后我们会讨论危险权限和普通权限)。在尝试调用需要用户尚未授予的权限的某个函数时,该函数将突然抛出异常(java.lang.SecurityException),导致应用程序崩溃。因此,我们需要在我们的应用程序中实现这个新的Android权限模型。
危险和常规安卓权限
Android将一些权限标记为危险权限,而将另一些标记为普通权限。这两类权限的共同之处在于需要在清单文件中进行定义。从Android 6.0开始,系统只会在运行时检查危险权限,而不会检查普通权限。例如,android.permission.INTERNET是一个普通权限。危险权限被分为不同的类别,以便用户更容易理解他们允许应用程序做什么。如果用户接受了一个组/类别中的一个权限,那么他们就接受了整个组。android.permission.FINE_LOCATION和android.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);
返回一个整数值,表示是否授予权限。注意:如果用户拒绝了应用中的关键权限,则应使用shouldShowRequestPermissionRationale(String permission)来说明需要权限的原因。让我们开发一个应用程序来检查权限是否已经存在。如果不存在,则在运行时请求权限。
Android Runtime Permissions 项目结构
Android运行时权限代码
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.Olivia.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.Olivia.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
请参考这个链接:https://developer.android.com/training/permissions/requesting.html