原文出处:http://jijiaxin89.com/2015/08/30/Android-s-Runtime-Permission/
PROTECTION_NORMAL类权限
当用户安装或更新应用时,系统将授予应用所请求的属于 PROTECTION_NORMAL 的所有权限(安装时授权的一类基本权限)。这类权限包括:
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
|
android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_NOTIFICATION_POLICY
android.permission.ACCESS_WIFI_STATE
android.permission.ACCESS_WIMAX_STATE
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.BROADCAST_STICKY
android.permission.CHANGE_NETWORK_STATE
android.permission.CHANGE_WIFI_MULTICAST_STATE
android.permission.CHANGE_WIFI_STATE
android.permission.CHANGE_WIMAX_STATE
android.permission.DISABLE_KEYGUARD
android.permission.EXPAND_STATUS_BAR
android.permission.FLASHLIGHT
android.permission.GET_ACCOUNTS
android.permission.GET_PACKAGE_SIZE
android.permission.INTERNET
android.permission.KILL_BACKGROUND_PROCESSES
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.NFC
android.permission.READ_SYNC_SETTINGS
android.permission.READ_SYNC_STATS
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.REORDER_TASKS
android.permission.REQUEST_INSTALL_PACKAGES
android.permission.SET_TIME_ZONE
android.permission.SET_WALLPAPER
android.permission.SET_WALLPAPER_HINTS
android.permission.SUBSCRIBED_FEEDS_READ
android.permission.TRANSMIT_IR
android.permission.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE_SYNC_SETTINGS
com.android.alarm.permission.SET_ALARM
com.android.launcher.permission.INSTALL_SHORTCUT
com.android.launcher.permission.UNINSTALL_SHORTCUT
|
只需要在AndroidManifest.xml中简单声明这些权限就好,安装时就授权。不需要每次使用时都检查权限,而且用户不能取消以上授权。
让你的app支持新运行时权限
是时候让我们的app支持新权限模型了,从设置compileSdkVersion andtargetSdkVersion 为 23开始吧.
1
2
3
4
5
6
7
8
9
|
android {
compileSdkVersion 23
...
defaultConfig {
...
targetSdkVersion 23
...
}
|
例子,我想用一下方法添加联系人。
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
|
private static final String TAG =
"Contacts"
;
private void insertDummyContact() {
// Two operations are needed to insert a new contact.
ArrayList
new
ArrayList
// First, set up a new raw contact.
ContentProviderOperation.Builder op =
ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE,
null
)
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME,
null
);
operations.add(op.build());
// Next, set the name for the contact.
op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
"__DUMMY CONTACT from runtime permissions sample"
);
operations.add(op.build());
// Apply the operations.
ContentResolver resolver = getContentResolver();
try
{
resolver.applyBatch(ContactsContract.AUTHORITY, operations);
}
catch
(RemoteException e) {
Log.d(TAG,
"Could not add a new contact: "
+ e.getMessage());
}
catch
(OperationApplicationException e) {
Log.d(TAG,
"Could not add a new contact: "
+ e.getMessage());
}
}
|
上面代码需要WRITE_CONTACTS权限。如果不询问授权,app就崩了。
下一步像以前一样在AndroidManifest.xml添加声明权限。
1
|
|
下一步,不得不再写个方法检查有没有权限。如果没有弹个对话框询问用户授权。然后你才可以下一步创建联系人。
权限被分组了,如下表:
同一组的任何一个权限被授权了,其他权限也自动被授权。例如,一旦WRITE_CONTACTS被授权了,app也有READ_CONTACTS和GET_ACCOUNTS了。
源码中被用来检查和请求权限的方法分别是Activity的checkSelfPermission和requestPermissions。这些方法api23引入。
1
2
3
4
5
6
7
8
9
10
11
|
final private int REQUEST_CODE_ASK_PERMISSIONS = 123;
private void insertDummyContactWrapper() {
int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_CONTACTS);
if
(hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
requestPermissions(
new
String[] {Manifest.permission.WRITE_CONTACTS},
REQUEST_CODE_ASK_PERMISSIONS);
return
;
}
insertDummyContact();
}
|
如果已有权限,insertDummyContact()会执行。否则,requestPermissions被执行来弹出请求授权对话框,如下:
不论用户同意还是拒绝,activity的onRequestPermissionsResult会被回调来通知结果(通过第三个参数),grantResults,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch
(requestCode) {
case
REQUEST_CODE_ASK_PERMISSIONS:
if
(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission Granted
insertDummyContact();
}
else
{
// Permission Denied
Toast.makeText(MainActivity.
this
,
"WRITE_CONTACTS Denied"
, Toast.LENGTH_SHORT)
.show();
}
break
;
default
:
super
.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
|
这就是新权限模型工作过程。代码真复杂但是只能去习惯它。。。为了让app很好兼容新权限模型,你不得不用以上类似方法处理所有需要的情况。
如果你想捶墙,现在是时候了。。。
处理 “不再提醒”
如果用户拒绝某授权。下一次弹框,用户会有一个“不再提醒”的选项的来防止app以后继续请求授权。
如果这个选项在拒绝授权前被用户勾选了。下次为这个权限请求requestPermissions时,对话框就不弹出来了,结果就是,app啥都不干。
这 将是很差的用户体验,用户做了操作却得不到响应。这种情况需要好好处理一下。在请求requestPermissions前,我们需要检查是否需要展示请 求权限的提示通过activity的shouldShowRequestPermissionRationale,代码如下:
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
|
final private int REQUEST_CODE_ASK_PERMISSIONS = 123;
private void insertDummyContactWrapper() {
int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_CONTACTS);
if
(hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
if
(!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {
showMessageOKCancel(
"You need to allow access to Contacts"
,
new
DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
requestPermissions(
new
String[] {Manifest.permission.WRITE_CONTACTS},
REQUEST_CODE_ASK_PERMISSIONS);
}
});
return
;
}
requestPermissions(
new
String[] {Manifest.permission.WRITE_CONTACTS},
REQUEST_CODE_ASK_PERMISSIONS);
return
;
}
insertDummyContact();
}
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new
AlertDialog.Builder(MainActivity.
this
)
.setMessage(message)
.setPositiveButton(
"OK"
, okListener)
.setNegativeButton(
"Cancel"
,
null
)
.create()
.show();
}
|
当一个权限第一次被请求和用户标记过不再提醒的时候,我们写的对话框被展示。
后一种情况,onRequestPermissionsResult 会收到PERMISSION_DENIED ,系统询问对话框不展示。
搞定!
一次请求多个权限
当然了有时候需要好多权限,可以用上面方法一次请求多个权限。不要忘了为每个权限检查“不再提醒”的设置。
修改后的代码:
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
|
final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;
private void insertDummyContactWrapper() {
List
new
ArrayList
final List
new
ArrayList
if
(!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION))
permissionsNeeded.add(
"GPS"
);
if
(!addPermission(permissionsList, Manifest.permission.READ_CONTACTS))
permissionsNeeded.add(
"Read Contacts"
);
if
(!addPermission(permissionsList, Manifest.permission.WRITE_CONTACTS))
permissionsNeeded.add(
"Write Contacts"
);
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) {
requestPermissions(permissionsList.toArray(
new
String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
}
});
return
;
}
requestPermissions(permissionsList.toArray(
new
String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
return
;
}
insertDummyContact();
}
private boolean addPermission(List
if
(checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(permission);
// Check for Rationale Option
if
(!shouldShowRequestPermissionRationale(permission))
return
false
;
}
return
true
;
}
|
如果所有权限被授权,依然回调onRequestPermissionsResult,我用hashmap让代码整洁便于阅读。
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
{
Map perms = new HashMap();
// Initial
perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.READ_CONTACTS, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.WRITE_CONTACTS, PackageManager.PERMISSION_GRANTED);
// Fill with results
for (int i = 0; i < permissions.length; i++)
perms.put(permissions[i], grantResults[i]);
// Check for ACCESS_FINE_LOCATION
if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.WRITE_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
// All Permissions Granted
insertDummyContact();
} else {
// Permission Denied
Toast.makeText(MainActivity.this, "Some Permission is Denied", Toast.LENGTH_SHORT)
.show();
}
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
条件灵活的,你自己设置。有的情况,一个权限没有授权,就不可用;但是也有情况,能工作,但是表现的是有所限制的。对于这个我不做评价,你自己设计吧