一、相关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.
* <p>
* 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.
* </p>
* <p>
* Call {@link #shouldShowRequestPermissionRationale(String)} before calling this API to
* check if the system recommends to show a rationale UI before asking for a permission.
* </p>
* <p>
* 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.
* </p>
* <p>
* Note that requesting a permission does not guarantee it will be granted and
* your app should be able to run without having this permission.
* </p>
* <p>
* 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[])}.
* </p>
* <p>
* When checking whether you have a permission you should use {@link
* #checkSelfPermission(String)}.
* </p>
* <p>
* You cannot request a permission if your activity sets {@link
* android.R.styleable#AndroidManifestActivity_noHistory noHistory} to
* <code>true</code> because in this case the activity would not receive
* result callbacks including {@link #onRequestPermissionsResult(int, String[], int[])}.
* </p>
* <p>
* The <a href="https://github.com/android/permissions-samples">
* RuntimePermissions</a> sample apps demonstrate how to use this method to
* request permissions at run time.
* </p>
*
* @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.
* <p>
* 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.
* <p>
* 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<out String>,
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
版权归原作者 小刘学安卓 所有, 如有侵权,请联系我们删除。