android-Android_6.0新特性之Runtime_Permission

Android 6.0 权限相关

Runtime Permissions

Android 6.0中谷歌摒弃了之前的install time permissions model取而代之的是runtime permissions model。先来说说install time permissions model,这个大家不陌生,就是当android App安装的时候会向用户展示一坨权限,如果此时用户选择安装,则表示用户同意将这些权限赋予App,如果用户不同意那么这个App就会取消安装。runtime permissions model就牛逼了,在App安装的时候同样会向用户展示所需要的权限,并且在用户选择安装App的时候并不表示用户将这些权限赋予了App,而是需要App在运行阶段主动去申请这些权限。这样做的好处显而易见,App对权限的申请对于用户来说变得更加透明,而且用户对App权限的控制也更加灵活。

权限的分类

Android将系统权限分成了四个保护等级normal,dangerous,signature,signatureOrSystem,其中最常见的是normal permissiondangerous permission两类。

normal permission涵盖的一系列权限的共同点是:App需要访问App运行沙盒以外的数据或资源,但是这些资源对用户的隐私或其他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
FLASHLIGHT
GET_PACKAGE_SIZE
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
SET_ALARM
INSTALL_SHORTCUT

以上这些就是Android 6.0中所有的normal permissions了。

dangerous permissions

dangerous permissions 涵盖的一系列权限的共同点是:这些权限会读写用户的隐私信息,也可能会读写用户存储的数据或影响其他App的正常运行。下面例举出这些权限:

权限组 权限
CALENDAR READ_CALENDARWRITE_CALENDAR
CAMERA CAMERA
CONTACTS READ_CONTACTSWRITE_CONTACTSGET_ACCOUNTS
LOCATION ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION
MICROPHONE RECORD_AUDIO
PHONE READ_PHONE_STATECALL_PHONE,READ_CALL_LOGWRITE_CALL_LOGADD_VOICEMAILUSE_SIPPROCESS_OUTGOING_CALLS
SENSORS BODY_SENSORS
SMS SEND_SMSRECEIVE_SMSREAD_SMSRECEIVE_WAP_PUSHRECEIVE_MMS
STORAGE READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE

以上这些权限就是Android6.0中所有的dangerous permissions

Runtime Permissions针对的是dangerous permissionsnormal permissions还是会在App安装期间被默认赋予。

实战

1、请求3个 dangerous permission,一定要在AndroidManifest.xml上配置,不然只在运行时请求会授权失败

1
2
3
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

2、java code,封装好的一些方法

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//---------------------- permission request
final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 163;
private void startAppWrapper() {
List<String> permissionsNeeded = new ArrayList<String>();

final List<String> permissionsList = new ArrayList<String>();
//只需要在这添加需要的dangerous permissions
if (!addPermission(permissionsList, Manifest.permission.WRITE_EXTERNAL_STORAGE))
permissionsNeeded.add("Storage");
if (!addPermission(permissionsList, Manifest.permission.READ_PHONE_STATE))
permissionsNeeded.add("Read Phone State");
if (!addPermission(permissionsList, Manifest.permission.GET_ACCOUNTS))
permissionsNeeded.add("Account Info");

if (permissionsList.size() > 0) {
if (permissionsNeeded.size() > 0) {
// Need Rationale
String message = "You need to grant access to " + permissionsNeeded.get(0);
for (int i = 1; i < permissionsNeeded.size(); i++)
message = message + ", " + permissionsNeeded.get(i);
showMessageOKCancel(message,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(Logo.this, permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
}
});
return;
}
ActivityCompat.requestPermissions(Logo.this, permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
return;
}

newStartApp();
}

private boolean addPermission(List<String> permissionsList, String permission) {
if (ContextCompat.checkSelfPermission(Logo.this, permission) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(permission);
// Check for Rationale Option
if (!ActivityCompat.shouldShowRequestPermissionRationale(Logo.this, permission))
return false;
}
return true;
}

private void showMessage(String message, DialogInterface.OnClickListener okListener, boolean isCancel) {
AlertDialog.Builder builder = new AlertDialog.Builder(Logo.this);

if (isCancel) {
builder.setNegativeButton("Cancel", null);
}

builder.setMessage(message)
.setPositiveButton("OK", okListener)
.create()
.show();
}

private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
showMessage(message, okListener, true);
}

private void showMessageOK(String message, DialogInterface.OnClickListener okListener) {
showMessage(message, okListener, false);
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
{
for (int i = 0; i < grantResults.length; i++) {
Log.e("--- asd", "--- asd: permission:"+permissions[i]+", result:"+ grantResults[i]);
}

boolean isAllAllow = true;
String notGranted = "";
for (int i = 0; i < permissions.length; i++) {
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
isAllAllow = false;
notGranted = permissions[i];
break;
}
}

if (isAllAllow) {
newStartApp();

} else {
// Permission Denied
showMessageOK("Grant the permissions for the game please.\n" + notGranted,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
}
}
break;
default:
break;
}
}

(ps: newStartApp() 方法是跳转到游戏的Acitivity的方法,先在请求玩所有游戏中需要用到的权限,全部授权成功后在跳转到游戏的Activity)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//进入app的第一个Activity的OnCreate方法
@Override
public void onCreate(Bundle savedInstanceState) {
Cocos2dxHelper.setContext(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.q2_logo);

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
newStartApp(); //如果api小与23,不需要运行请求权限
return;
}

startAppWrapper();
}

运行

img