权限分为几个保护级别。保护级别影响着是否需要运行时权限请求。
有三种保护级别会影响第三方应用:普通、签名和危险权限。如需查看特定权限所拥有的保护级别,请访问权限 API 参考页面。
普通权限
普通权限涵盖以下情况:应用需要访问其沙盒外部的数据或资源,但对用户隐私或其他应用的操作带来的风险很小。例如,设置时区的权限就是普通权限。
如果应用在清单中声明需要普通权限,系统会在安装时自动向应用授予该权限。系统不会提示用户授予普通权限,用户也无法撤消这些权限。
签名权限
系统在安装时授予这些应用权限,但仅会在尝试使用某权限的应用签名证书为定义该权限的同一证书时才会授予。
注意:有些签名权限不适合第三方应用使用。
危险权限
危险权限涵盖以下情况:应用需要的数据或资源涉及用户隐私信息,或者可能对用户存储的数据或其他应用的操作产生影响。例如,能够读取用户的联系人属于危险权限。如果应用声明其需要危险权限,必须由用户向应用明确授予该权限。在用户批准该权限之前,应用无法提供依赖于该权限的功能。
为了使用危险权限,应用必须在运行时提示用户授予权限。如需详细了解如何提示用户,请参阅危险权限的请求提示。
特殊权限
有几项权限的行为与普通权限及危险权限都不同。[SYSTEM_ALERT_WINDOW](https://developer.android.google.cn/reference/android/Manifest.permission#SYSTEM_ALERT_WINDOW)
和 [WRITE_SETTINGS](https://developer.android.google.cn/reference/android/Manifest.permission#WRITE_SETTINGS)
特别敏感,因此大多数应用不应该使用它们。
在大多数情况下,应用必须在清单中声明 SYSTEM_ALERT_WINDOW
权限,并发送请求用户授权的 intent。系统将向用户显示详细管理屏幕,以响应该 intent。
从 Android 11(API 级别 30)开始,调用 ACTION_MANAGE_OVERLAY_PERMISSION
intent 操作的应用不能指定软件包。当应用调用包含此 intent 操作的 intent 时,必须由用户先选择想要授予或撤消哪些应用的权限。此行为可以让权限的授予更有目的性,从而达到保护用户的目的。
如需详细了解如何请求这些权限,请参阅 [SYSTEM_ALERT_WINDOW](https://developer.android.google.cn/reference/android/Manifest.permission#SYSTEM_ALERT_WINDOW)
和 [WRITE_SETTINGS](https://developer.android.google.cn/reference/android/Manifest.permission#WRITE_SETTINGS)
参考条目。
如需了解 Android 系统提供的所有权限,请参阅 [Manifest.permission](https://developer.android.google.cn/reference/android/Manifest.permission)
。
权限检测方式:
/**
* Determine whether <em>you</em> have been granted a particular permission.
*
* @param permission The name of the permission being checked.
*
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if you have the
* permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} if not.
*
* @see android.content.pm.PackageManager#checkPermission(String, String)
*/
public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
}
权限获取方式:
/**
* 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>
* 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 reporting whether the
* permissions were granted or not. Your activity has to implement {@link
* androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback}
* and the results of permission requests will be delivered to its {@link
* androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(
* int, String[], int[])} method.
* </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 your
* {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])}.
* </p>
* <p>
* When checking whether you have a permission you should use {@link
* #checkSelfPermission(android.content.Context, String)}.
* </p>
* <p>
* Calling this API for permissions already granted to your app would show UI
* to the user to decided whether the app can still hold these permissions. This
* can be useful if the way your app uses the data guarded by the permissions
* changes significantly.
* </p>
* <p>
* You cannot request a permission if your activity sets {@link
* android.R.attr#noHistory noHistory} to <code>true</code> in the manifest
* because in this case the activity would not receive result callbacks including
* {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])}.
* </p>
* <p>
* The <a href="https://github.com/googlesamples/android-RuntimePermissions">
* RuntimePermissions</a> sample app demonstrates how to use this method to
* request permissions at run time.
* </p>
*
* @param activity The target activity.
* @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 OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])}.
* Should be >= 0.
*
* @see OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])
* @see #checkSelfPermission(android.content.Context, String)
* @see #shouldShowRequestPermissionRationale(android.app.Activity, String)
*/
public static void requestPermissions(final @NonNull Activity activity,
final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode) {
if (sDelegate != null
&& sDelegate.requestPermissions(activity, permissions, requestCode)) {
// Delegate has handled the permission request.
return;
}
if (Build.VERSION.SDK_INT >= 23) {
if (activity instanceof RequestPermissionsRequestCodeValidator) {
((RequestPermissionsRequestCodeValidator) activity)
.validateRequestPermissionsRequestCode(requestCode);
}
activity.requestPermissions(permissions, requestCode);
} else if (activity instanceof OnRequestPermissionsResultCallback) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
final int[] grantResults = new int[permissions.length];
PackageManager packageManager = activity.getPackageManager();
String packageName = activity.getPackageName();
final int permissionCount = permissions.length;
for (int i = 0; i < permissionCount; i++) {
grantResults[i] = packageManager.checkPermission(
permissions[i], packageName);
}
((OnRequestPermissionsResultCallback) activity).onRequestPermissionsResult(
requestCode, permissions, grantResults);
}
});
}
}
获取权限时用户拒绝如何处理:
弹出自定义提示信息框说明权限申请的用途,告知用户拒绝权限申请的影响。
/**
* Gets whether you should show UI with rationale for requesting a permission.
* You should do this only if you do not have the permission and the context in
* which the permission is requested does not clearly communicate to the user
* what would be the benefit from granting this permission.
* <p>
* For example, if you write a camera app, requesting the camera permission
* would be expected by the user and no rationale for why it is requested is
* needed. If however, the app needs location for tagging photos then a non-tech
* savvy user may wonder how location is related to taking photos. In this case
* you may choose to show UI with rationale of requesting this permission.
* </p>
*
* @param activity The target activity.
* @param permission A permission your app wants to request.
* @return Whether you can show permission rationale UI.
*
* @see #checkSelfPermission(android.content.Context, String)
* @see #requestPermissions(android.app.Activity, String[], int)
*/
public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
@NonNull String permission) {
if (Build.VERSION.SDK_INT >= 23) {
return activity.shouldShowRequestPermissionRationale(permission);
}
return false;
}
权限的实际处理:
class TestPermissionActivity : AppCompatActivity() {
private val permissionList = arrayListOf(
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val requestPermissions = arrayListOf<String>()
permissionList.forEach {
if (ActivityCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED) {
requestPermissions.add(it)
}
}
if (!requestPermissions.isNullOrEmpty()) {
ActivityCompat.requestPermissions(this, requestPermissions.toTypedArray(), 1000)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == 1000) {
for (i in permissions.indices) {
val hasPermission = grantResults[i] == PackageManager.PERMISSION_GRANTED
val toastString =
"${permissions[i]}权限获取${if (hasPermission) "成功" else "失败"}"
Toast.makeText(this, toastString, Toast.LENGTH_LONG).show()
if (!hasPermission) {
val canShow =
ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])
if (canShow) {
//自定义提示信息
AlertDialog.Builder(this).create().show()
}
}
}
}
}
}