动态权限

动态权限

动态权限又称运行时权限(高于 6.0 版本)。如果设备是运行在 Android 6.0 版本以上 —— app 的目标 sdk 版本是 23 以上时,在安装时,任何 app 权限用户都没有通知。在运行时,你的 app 必须请求用户授予危险权限,用户会看到一个系统弹窗(见下图左边)告诉用户,你的 app 正试图访问那个权限组。弹窗包括拒绝允许按钮。

运行时权限弹窗

如果用户拒绝权限请求,下一次你的 app 请求权限,弹窗会包含一个复选框,当这个复选框被选中,表示了用户不想在提示这个权限(见上图右边)。

如果用户选中 Never ask again 并且点击 DENY,那么如果你之后试图请求相同的权限,系统将不再展示给用户。

及时用户授予即使用户授予您的应用程序所请求的权限,您也不能总是依赖它,用户还可以选择在系统设置中逐个启用和禁用权限,您应始终在运行时检查并请求权限以防止运行时错误。

动态权限的分类

权限分为几个保护等级。保护等级会影响是否需要运行时权限请求。

有三个保护等级会影响第三方应用程序。普通(normal)签名(signature)危险(dangerous)权限。

普通权限

普通权限覆盖了你的 app 需要在沙盒外访问的数据或资源的区域,但是对用户的隐私或其他应用程序的操作风险很小。例如,设置设置时区的权限是普通权限。

如果一个 app 在它的清单文件中声明它需要一个普通权限,系统自动在安装时授权 app 权限。系统不会提示用户授权普通权限,并且用户不能撤销这些权限。

在 Android 8.1 版本(API 等级 27)中,下列是正常权限的分类:

  • 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
  • MANAGE_OWN_CALLS
  • MODIFY_AUDIO_SETTINGS
  • NFC
  • READ_SYNC_SETTINGS
  • READ_SYNC_STATS
  • RECEIVE_BOOT_COMPLETED
  • REORDER_TASKS
  • REQUEST_COMPANION_RUN_IN_BACKGROUND
  • REQUEST_COMPANION_USE_DATA_IN_BACKGROUND
  • REQUEST_DELETE_PACKAGES
  • REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
  • SET_ALARM
  • SET_WALLPAPER
  • SET_WALLPAPER_HINTS
  • TRANSMIT_IR
  • USE_FINGERPRINT
  • VIBRATE
  • WAKE_LOCK
  • WRITE_SYNC_SETTINGS

签名权限

但是当且仅当尝试使用权限的应用程序使用与定义权限的应用程序相同的证书进行签名时,系统在安装时授权app 这些权限。

注意:一些签名权限不能通过第三方应用使用。

在 Android 8.1 版本(API 等级 27)中,下列第三方程序可以使用的权限的签名权限分类:

危险权限

危险权限涵盖 app 想要调用用户的隐私信息,或者可能影响用户的存储数据或操作其他应用程序的区域。例如:读取用户联系人的能力是一个危险权限。如果一个 app 声明它需要一个危险权限,用户必须显示的授予 app 权限。直到用户批准权限,你的 app 才能提供依赖该权限的功能。

为了使用危险权限,你的 app 必须在运行时提示用户授权权限。

危险权限列表,见下表。

权限组 权限
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

如何申请动态权限

在清单文件中添加权限

1
<uses-permission android:name="android.permission.CALL_PHONE"/>

检查权限

1
2
3
4
if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
}

请求权限

1
ActivityCompat.requestPermissions(MainActivity.this, new String[]{ Manifest.permission.CALL_PHONE}, 1);

处理权限请求相应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
Log.d(TAG, "onRequestPermissionsResult: ");
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager
.PERMISSION_GRANTED) {
call();
} else {
Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
}
default:
}
}

请求拨打电话完整代码

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = findViewById(R.id.button);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "click button", Toast.LENGTH_SHORT).show();
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{
Manifest.permission.CALL_PHONE
}, 1);
} else {
call();
}
}
});
}
private void call() {
intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Log.d(TAG, "onRequestPermissionsResult: ");
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager
.PERMISSION_GRANTED) {
call();
} else {
Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
}
default:
}
}
}
坚持原创技术分享,您的支持将鼓励我继续创作!