[置顶] Android开发必备之运行时动态请求权限

时间:2023-01-08 17:44:43

从 Android 6.0(API 级别 23)开始,用户开始在应用运行时向其授予权限,而不是在应用安装时授予。此方法可以简化应用安装过程,因为用户在安装或更新应用时不需要授予权限。它还让用户可以对应用的功能进行更多控制;例如,用户可以选择为相机应用提供相机访问权限,而不提供设备位置的访问权限。用户可以随时进入应用的“Settings”界面调用权限。
如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在安装时要求用户授予权限。再次强调,系统只告诉用户应用需要的权限组,而不告知具体权限。所以如果不想动态获取权限的话,可以设置 targetSdkVersion 小于23即可

系统权限分为两类:正常权限和危险权限:

  1. 正常权限不会直接给用户隐私权带来风险。如果您的应用在其清单(Manifest)中列出了正常权限,系统将自动授予该权限。
  2. 危险权限会授予应用访问用户机密数据的权限。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。如果您列出了危险权限,则用户必须明确批准您的应用使用这些权限。

正常的权限

  1. ACCESS_LOCATION_EXTRA_COMMANDS
  2. ACCESS_NETWORK_STATE
  3. ACCESS_NOTIFICATION_POLICY
  4. ACCESS_WIFI_STATE
  5. BLUETOOTH
  6. BLUETOOTH_ADMIN
  7. BROADCAST_STICKY
  8. CHANGE_NETWORK_STATE
  9. CHANGE_WIFI_MULTICAST_STATE
  10. CHANGE_WIFI_STATE
  11. DISABLE_KEYGUARD
  12. EXPAND_STATUS_BAR
  13. GET_PACKAGE_SIZE
  14. INSTALL_SHORTCUT
  15. INTERNET
  16. KILL_BACKGROUND_PROCESSES
  17. MODIFY_AUDIO_SETTINGS
  18. NFC
  19. READ_SYNC_SETTINGS
  20. READ_SYNC_STATS
  21. RECEIVE_BOOT_COMPLETED
  22. REORDER_TASKS
  23. REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
  24. REQUEST_INSTALL_PACKAGES
  25. SET_ALARM
  26. SET_TIME_ZONE
  27. SET_WALLPAPER
  28. SET_WALLPAPER_HINTS
  29. TRANSMIT_IR
  30. UNINSTALL_SHORTCUT
  31. USE_FINGERPRINT
  32. VIBRATE
  33. WAKE_LOCK
  34. WRITE_SYNC_SETTINGS
    危险权限表格
    [置顶]        Android开发必备之运行时动态请求权限

所有危险的 Android 系统权限都属于权限组。如果设备运行的是 Android 6.0(API 级别 23),并且应用的 targetSdkVersion 是 23 或更高版本,则当用户请求危险权限时系统会发生以下行为:

  1. 如果应用请求其清单中列出的危险权限,而应用目前在权限组中没有任何权限,则系统会向用户显示一个对话框,描述应用要访问的权限组。对话框不描述该组内的具体权限。例如,如果应用请求 READ_CONTACTS 权限,系统对话框只说明该应用需要访问设备的联系信息。如果用户批准,系统将向应用授予其请求的权限。
  2. 如果应用请求其清单中列出的危险权限,而应用在同一权限组中已有另一项危险权限,则系统会立即授予该权限,而无需与用户进行任何交互。例如,如果某应用已经请求并且被授予了 READ_CONTACTS 权限,然后它又请求 WRITE_CONTACTS,系统将立即授予该权限

本人封装的一个简单请求权限的类

**
* Created by jian on 2017/4/12.
*/

public class permissionUtil {

/**
* 返回时、否拥有改权限
* @param activity
* @param permission
* @return
*/

public static boolean isOwnPermisson(Activity activity, String permission){
return ContextCompat.checkSelfPermission(activity,
permission)
== PackageManager.PERMISSION_GRANTED;
}
public static void requestPermission(Activity activity, String permission,int requestCode) {
if (ContextCompat.checkSelfPermission(activity,
permission)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
permission)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.

} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(activity,
new String[]{permission},//需要请求多个权限,可以在这里添加
requestCode);

}
}
}
}

Activity中使用

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

if (permissionUtil.isOwnPermisson(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
//如果已经拥有改权限
Log.i("request","own");
} else {
//没有改权限,需要进行请求
permissionUtil.requestPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE, REQUEST_CODE);
}
}

private final int REQUEST_CODE = 2;//请求码

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_CODE: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i("request", "success");
} else {
Log.i("request", "failed");
}
return;
}
}
}
}

运行结果
[置顶]        Android开发必备之运行时动态请求权限
点击allow后
[置顶]        Android开发必备之运行时动态请求权限
如果你不知道有什么权限,请看上面的表格,或查看Manifest.permission 的源码

我们来具体分析一下用法。

检查权限

如果您的应用需要危险权限,则每次执行需要这一权限的操作时您都必须检查自己是否具有该权限。用户始终可以*调用此权限,因此,即使应用昨天使用了相机,它不能假设自己今天仍具有该权限。

要检查您是否具有某项权限,请调用

ContextCompat.checkSelfPermission()

方法。例如,以下代码段显示了如何检查 Activity 是否具有在内存中进行写入的权限:

ContextCompat.checkSelfPermission(activity,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED

如果应用具有此权限,方法将返回 PackageManager.PERMISSION_GRANTED,并且应用可以继续操作。如果应用不具有此权限,方法将返回 PackageManager.PERMISSION_DENIED,且应用必须明确向用户要求权限。

请求您需要的权限

如果应用尚无所需的权限,则应用必须调用一个 requestPermissions() 方法,以请求适当的权限。应用将传递其所需的权限,以及您指定用于识别此权限请求的整型请求代码。此方法异步运行:它会立即返回,并且在用户响应对话框之后,系统会使用结果调用应用的回调方法,将应用传递的相同请求代码传递到 requestPermissions()。

以下代码(来自官网)可以检查应用是否具备读取用户联系人的权限,并根据需要请求该权限:

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {

// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {

// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.

} else {

// No explanation needed, we can request the permission.

ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);

// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}

处理权限请求响应

 @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_CODE: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i("request", "success");
} else {
Log.i("request", "failed");
}
return;
}
}
}

欢迎留言讨论