Android 运行时权限

在Android6.0(API-23)以上且 targetSdkVersion 是23时申请危险权限需要动态申请。

如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本则不需要申请权限。

危险权限包括

权限组 权限
CALENDAR READ_CALENDAR WRITE_CALENDAR
CAMERA CAMERA
CONTACTS READ_CONTACTS WRITE_CONTACTS GET_ACCOUNTS
LOCATION ACCESS_FINE_LOCATION ACCESS_COARSE_LOCATION
MICROPHONE RECORD_AUDIO
PHONE READ_PHONE_STATE CALL_PHONE READ_CALL_LOG WRITE_CALL_LOG ADD_VOICEMAIL USE_SIP PROCESS_OUTGOING_CALLS
SENSORS BODY_SENSORS
SMS SEND_SMS RECEIVE_SMS READ_SMS RECEIVE_WAP_PUSH RECEIVE_MMS
STORAGE READ_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE

普通权限(只要App在清单中列出就会自动授权)

  • ACCESS_LOCATION_EXTRA_COMMANDS
  • ACCESS_NETWORK_STATE
  • ACCESS_NOTIFICATION_POLICY
  • ACCESS_WIFI_STATE
  • BLUETOOTH
  • BLUETOOTH_ADMIN
  • BROADCAST_STICKY
  • CHANGE_NETWORK_STATE
  • CHANGE_WIFI_MULTICAST_STATE
  • CHANGE_WIFI_STATE
  • DISABLE_KEYGUARD
  • EXPAND_STATUS_BAR
  • GET_PACKAGE_SIZE
  • INSTALL_SHORTCUT
  • INTERNET
  • KILL_BACKGROUND_PROCESSES
  • MODIFY_AUDIO_SETTINGS
  • NFC
  • READ_SYNC_SETTINGS
  • READ_SYNC_STATS
  • RECEIVE_BOOT_COMPLETED
  • REORDER_TASKS
  • REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
  • REQUEST_INSTALL_PACKAGES
  • SET_ALARM
  • SET_TIME_ZONE
  • SET_WALLPAPER
  • SET_WALLPAPER_HINTS
  • TRANSMIT_IR
  • UNINSTALL_SHORTCUT
  • USE_FINGERPRINT
  • VIBRATE
  • WAKE_LOCK
  • WRITE_SYNC_SETTINGS

运行时权限的几个基本方法

API 作用
checkSelfPermission() 判断权限是否具有某项权限
requestPermissions() 申请权限
onRequestPermissionsResult() 申请权限回调方法
shouldShowRequestPermissionRationale() 是否要提示用户申请该权限的缘由,该方法只有当用户第一次拒绝之后再请求时会返回true 如果用户勾选不再询问之后会返回false

运行时权限实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) !=
PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest
.permission.READ_PHONE_STATE)) {
// 用户首次拒绝权限第二次请求的时候会执行这段代码,可以在这段代码里弹出对话框解释申请权限的原因并且引导用户再次申请权限
Toast.makeText(this, "show reason", Toast.LENGTH_SHORT).show();
} else {
// 第一次直接申请权限
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission
.READ_PHONE_STATE}, REQ_CODE);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQ_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限获取成功
Toast.makeText(this, "权限获取成功", Toast.LENGTH_SHORT).show();
} else {
// 权限获取失败
Toast.makeText(this, "权限获取失败", Toast.LENGTH_SHORT).show();
}
}
}

图例

第一次请求流程

第一次请求

第二次请求流程

第二次请求

第三次请求流程

第三次请求

流程表

序号 用户是否授予权限 shouldShowRequestPermissionRationale() 返回 是否勾选“不再询问”
1 false -
2 true
3 true
…………i true
i + 1 -false - -

EasyPermisions

运行时权限结合EasyPermisions使用。
EasyPermisions是谷歌开源的运行时权限封装库,使用时在BaseActivity和BaseFragment中加上就可以轻松使用运行时权限。

BaseActivity封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public abstract class BaseActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks{
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 处理动态权限
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
// 获得授权
}
@Override
public void onPermissionsGranted(int requestCode, List<String> perms) {
// 权限被拒绝
}
}

BaseFragment封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public abstract class BaseFragment extends Fragment implements EasyPermissions.PermissionCallbacks {
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 处理动态权限
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
// 获得授权
}
@Override
public void onPermissionsGranted(int requestCode, List<String> perms) {
// 权限被拒绝
}
}

使用例子

1
2
3
4
5
6
if (EasyPermissions.hasPermissions("Activity or Fragment", "权限数组")) {
// 已经获取权限
} else {
// 申请权限
EasyPermissions.requestPermissions("Activity or Fragment", "再次申请权限的理由(String)", "标识此次申请的code(int)", "权限数组(String[])");
}

参考

Android M 权限最佳实践