android 动态权限申请 android 动态权限申请

一、相关api介绍

        1、requestPermissions

/**
 * Requests permissions to be granted to this application. These permissions
 * must be requested in your manifest, they should not be granted to your app,
 * and they should have protection level {@link
 * android.content.pm.PermissionInfo#PROTECTION_DANGEROUS dangerous}, regardless
 * whether they are declared by the platform or a third-party app.
 * 
 * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
 * are granted at install time if requested in the manifest. Signature permissions
 * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
 * install time if requested in the manifest and the signature of your app matches
 * the signature of the app declaring the permissions.
 * 
 * 
 * Call {@link #shouldShowRequestPermissionRationale(String)} before calling this API to
 * check if the system recommends to show a rationale UI before asking for a permission.
 * 
 * 
 * If your app does not have the requested permissions the user will be presented
 * with UI for accepting them. After the user has accepted or rejected the
 * requested permissions you will receive a callback on {@link
 * #onRequestPermissionsResult(int, String[], int[])} reporting whether the
 * permissions were granted or not.
 * 
 * 
 * Note that requesting a permission does not guarantee it will be granted and
 * your app should be able to run without having this permission.
 * 
 * 
 * This method may start an activity allowing the user to choose which permissions
 * to grant and which to reject. Hence, you should be prepared that your activity
 * may be paused and resumed. Further, granting some permissions may require
 * a restart of you application. In such a case, the system will recreate the
 * activity stack before delivering the result to {@link
 * #onRequestPermissionsResult(int, String[], int[])}.
 * 
 * 
 * When checking whether you have a permission you should use {@link
 * #checkSelfPermission(String)}.
 * 
 * 
 * You cannot request a permission if your activity sets {@link
 * android.R.styleable#AndroidManifestActivity_noHistory noHistory} to
 * true because in this case the activity would not receive
 * result callbacks including {@link #onRequestPermissionsResult(int, String[], int[])}.
 * 
 * 
 * The 
 * RuntimePermissions sample apps demonstrate how to use this method to
 * request permissions at run time.
 * 
 *
 * @param permissions The requested permissions. Must me non-null and not empty.
 * @param requestCode Application specific request code to match with a result
 * reported to {@link #onRequestPermissionsResult(int, String[], int[])}.
 * Should be >= 0.
 *
 * @throws IllegalArgumentException if requestCode is negative.
 *
 * @see #onRequestPermissionsResult(int, String[], int[])
 * @see #checkSelfPermission(String)
 * @see #shouldShowRequestPermissionRationale(String)
 */
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
 if (requestCode < 0) {
 throw new IllegalArgumentException("requestCode should be >= 0");
 }
 if (mHasCurrentPermissionsRequest) {
 Log.w(TAG, "Can request only one set of permissions at a time");
 // Dispatch the callback with empty arrays which means a cancellation.
 onRequestPermissionsResult(requestCode, new String[0], new int[0]);
 return;
 }
 if (!getAttributionSource().getRenouncedPermissions().isEmpty()) {
 final int permissionCount = permissions.length;
 for (int i = 0; i < permissionCount; i++) {
 if (getAttributionSource().getRenouncedPermissions().contains(permissions[i])) {
 throw new IllegalArgumentException("Cannot request renounced permission: "
 + permissions[i]);
 }
 }
 }
 final Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
 startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
 mHasCurrentPermissionsRequest = true;
}

注释翻译如下:

        该函数作用是请求授予应用程序权限。不管这些权限是否被平台或三方应用声明过,他们都必须在manifest清单中进行申请,他们应该没有被授予给应用程序,并且这些权限应该具有危险的保护级别

        如果在manifest中声明了普通权限(正常保护级别),则在安装应用的时就会授予这些普通权限给应用。如果签名权限(签名保护级别)在manifest中声明了,并app的签名和app声明权限的签名匹配,则在安装应用程序时会授予签名权限。

        在调用requestPermissions()函数之前调用shouldShowRequestPermissionRationale(String)来检查在询问权限前,系统是否推荐展示请求权限原因的UI。

        如果你的app没有这些被请求的权限,为了接受这些权限,将会展示UI给用户。在用户接受或拒绝这些请求的权限之后,你将收到一个回调函数onRequestPermissionsResult(int, String[], int[])来报告这些权限是否被授予或没有被授予。

        注意,请求一个权限不能保证权限被授予,并且你的应用app应该有能力在不具有这些权限的情况下正常运行。

        该函数可能启动一个activity允许用户选择哪个权限被授予,哪个权限被拒绝。因此,您应该预料到您的activity可能会被暂停和resumed。更进一步,授予某些权限可能需要应用程序进行重启。在这种情况下,在把结果交付给onRequestPermissionsResult(int, String[], int[])前,系统将重建activity栈。

        当检查是否你具有某个权限,你应该使用checkSelfPermission(String)。

        如果你的activity设置了 android.R.styleable#AndroidManifestActivity_noHistory noHistory=true,你将不能请求权限,因为在这种情况下activity将不会接受onRequestPermissionsResult(int, String[], int[])中的回调结果。

        链接https://github.com/android/permissions-samples的例子RuntimePermissions演示了在运行时,如何使用该方法请求权限。

demo演示

package com.example.helloworld.activity.camera;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import com.example.helloworld.R;
import com.google.android.material.snackbar.Snackbar;
/**
 * Launcher Activity that demonstrates the use of runtime permissions for Android M.
 * This Activity requests permissions to access the camera
 * ({@link android.Manifest.permission#CAMERA})
 * when the 'Show Camera Preview' button is clicked to start {@link CameraPreviewActivity} once
 * the permission has been granted.
 * 
 * First, the status of the Camera permission is checked using {@link
 * ActivityCompat#checkSelfPermission(Context, String)}
 * If it has not been granted ({@link PackageManager#PERMISSION_GRANTED}), it is requested by
 * calling
 * {@link ActivityCompat#requestPermissions(Activity, String[], int)}. The result of the request is
 * returned to the
 * {@link androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback}, which starts
 * {@link
 * CameraPreviewActivity} if the permission has been granted.
 * 
 * Note that there is no need to check the API level, the support library
 * already takes care of this. Similar helper methods for permissions are also available in
 * ({@link ActivityCompat},
 * {@link androidx.core.content.ContextCompat} and {@link androidx.fragment.app.Fragment}).
 */
public class MainPermissionActivity extends AppCompatActivity
 implements ActivityCompat.OnRequestPermissionsResultCallback {
 private static final int PERMISSION_REQUEST_CAMERA = 0;
 private View mLayout;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main1);
 mLayout = findViewById(R.id.main_layout);
 // Register a listener for the 'Show Camera Preview' button.
 findViewById(R.id.button_open_camera).setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View view) {
 showCameraPreview();
 }
 });
 }
 @Override
 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
 @NonNull int[] grantResults) {
 super.onRequestPermissionsResult(requestCode, permissions, grantResults);
 // BEGIN_INCLUDE(onRequestPermissionsResult)
 if (requestCode == PERMISSION_REQUEST_CAMERA) {
 // Request for camera permission.
 if (grantResults.length == 1 & grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 // Permission has been granted. Start camera preview Activity.
 Snackbar.make(mLayout, R.string.camera_permission_granted,
 Snackbar.LENGTH_LONG)
 .show();
 startCamera();
 } else {
 // Permission request was denied.
 Snackbar.make(mLayout, R.string.camera_permission_denied,
 Snackbar.LENGTH_LONG)
 .show();
 }
 }
 // END_INCLUDE(onRequestPermissionsResult)
 }
 private void showCameraPreview() {
 // BEGIN_INCLUDE(startCamera)
 // Check if the Camera permission has been granted
 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
 == PackageManager.PERMISSION_GRANTED) {
 // Permission is already available, start camera preview
 Snackbar.make(mLayout,
 R.string.camera_permission_available,
 Snackbar.LENGTH_LONG).show();
 startCamera();
 } else {
 // Permission is missing and must be requested.
 requestCameraPermission();
 }
 // END_INCLUDE(startCamera)
 }
 /**
 * Requests the {@link android.Manifest.permission#CAMERA} permission.
 * If an additional rationale should be displayed, the user has to launch the request from
 * a SnackBar that includes additional information.
 */
 private void requestCameraPermission() {
 // Permission has not been granted and must be requested.
 if (ActivityCompat.shouldShowRequestPermissionRationale(this,
 Manifest.permission.CAMERA)) {
 // Provide an additional rationale to the user if the permission was not granted
 // and the user would benefit from additional context for the use of the permission.
 // Display a SnackBar with cda button to request the missing permission.
 Snackbar.make(mLayout, R.string.camera_access_required,
 Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok, new View.OnClickListener() {
 @Override
 public void onClick(View view) {
 // Request the permission
 ActivityCompat.requestPermissions(MainPermissionActivity.this,
 new String[]{Manifest.permission.CAMERA},
 PERMISSION_REQUEST_CAMERA);
 }
 }).show();
 } else {
 Snackbar.make(mLayout, R.string.camera_unavailable, Snackbar.LENGTH_LONG).show();
 // Request the permission. The result will be received in onRequestPermissionResult().
 ActivityCompat.requestPermissions(this,
 new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
 }
 }
 private void startCamera() {
 Intent intent = new Intent(this, CameraPreviewActivity.class);
 startActivity(intent);
 }
}

2、shouldShowRequestPermissionRationale   

/**
 * Gets whether you should show UI with rationale before requesting a permission.
 *
 * @param permission A permission your app wants to request.
 * @return Whether you should show permission rationale UI.
 *
 * @see #checkSelfPermission(String)
 * @see #requestPermissions(String[], int)
 * @see #onRequestPermissionsResult(int, String[], int[])
 */
public boolean shouldShowRequestPermissionRationale(@NonNull String permission) {
 return getPackageManager().shouldShowRequestPermissionRationale(permission);
}

        1、应用第一次申请危险权限时,shouldShowRequestPermissionRationale(permission)=false,不会弹出请求权限原因说明弹窗,而是直接弹出请求权限弹窗让用户选择授予或拒绝权限

         2、当且仅当用户之前有拒绝了危险权限,且没有勾选”拒绝且不再询问“复选框时shouldShowRequestPermissionRationale(permission)=true,则申请危险权限时,会先弹出请求权限原因说明弹窗,在弹窗允许后才会弹出请求权限弹窗让用户选择授予或拒绝权限;

        3、如果用户勾选了”拒绝且不再询问“复选框,则shouldShowRequestPermissionRationale(permission)=false,则申请危险权限时,不再弹出权限原因说明弹窗,并且调用requestPermissions()申请危险权限时也不再弹出请求权限弹窗,而是直接走到onRequestPermissionsResult()函数中权限请求失败的逻辑。

       疑问:为什么勾选了”拒绝且不再询问“复选框后再请求权限就不会再弹窗请求权限弹窗,我没有从源码中看出是如何实现的。

注意:上面的逻辑我是在android api 28上能够复现的,在api30上好像不行(api 30 没有”拒绝且不再询问“复选框),具体原因不清楚。

相关代码如下:

reqoneper.setOnClickListener {
 if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
 //if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
 if (shouldShowRequestPermissionRationale( Manifest.permission.CAMERA)) {
 var dialog = AlertDialog.Builder(this)
 .setTitle("权限申请原因")
 .setMessage("您必须申请相机权限才能正常使用我们的功能,请开启相机权限")
 .setPositiveButton(
 "允许",
 DialogInterface.OnClickListener() { dialog, which ->
 requestPermissions(
 onePermission,
 reqonecode
 )
 })
 .setNegativeButton(
 "取消",
 DialogInterface.OnClickListener() { dialog, which ->
 dialog.cancel()
 }).create()
 dialog.setCanceledOnTouchOutside(false)
 dialog.show()
 } else {
 requestPermissions(onePermission, reqonecode)
 }
 } else {
 openCamera()
 }
}
override fun onRequestPermissionsResult(
 requestCode: Int,
 permissions: Array,
 grantResults: IntArray
) {
 super.onRequestPermissionsResult(requestCode, permissions, grantResults)
 when (requestCode) {
 reqonecode -> {
 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 openCamera()
 } else {
 Toast.makeText(this, "申请相机失败", Toast.LENGTH_SHORT).show()
 }
 }
 }
}

参考文章:

Android内部分享[8]——Android系统的应用程序权限申请 - DP2PX.COM

作者:小刘学安卓原文地址:https://blog.csdn.net/liuqinhou/article/details/127187327

%s 个评论

要回复文章请先登录注册