Runtime Permissions
Android 6.0的发布,有一个重大的改变:Android系统加强了对App的权限管理,带来的好处是用户可以自行管理应用的权限而不再是一个旁观者。与此同时,对于开发者而言,在开发的过程中需要适配。
新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。Normal Permissions会在安装应用时默认授予,而Dangerous Permission需要在使用到这个权限时对用户进行请求授予。
- Normal Permissions
- 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_INSTALL_PACKAGES
- SET_ALARM
- SET_TIME_ZONE
- SET_WALLPAPER
- SET_WALLPAPER_HINTS
- TRANSMIT_IR
- UNINSTALL_SHORTCUT
- USE_FINGERPRINT
- VIBRATE
- WAKE_LOCK
- WRITE_SYNC_SETTINGS
- Dangerous Permission
- group:android.permission-group.CONTACTS
- permission:android.permission.WRITE_CONTACTS
- permission:android.permission.GET_ACCOUNTS
- permission:android.permission.READ_CONTACTS
- group:android.permission-group.PHONE
- permission:android.permission.READ_CALL_LOG
- permission:android.permission.READ_PHONE_STATE
- permission:android.permission.CALL_PHONE
- permission:android.permission.WRITE_CALL_LOG
- permission:android.permission.USE_SIP
- permission:android.permission.PROCESS_OUTGOING_CALLS
- permission:com.android.voicemail.permission.ADD_VOICEMAIL
- group:android.permission-group.CALENDAR
- permission:android.permission.READ_CALENDAR
- permission:android.permission.WRITE_CALENDAR
- group:android.permission-group.CAMERA
- permission:android.permission.CAMERA
- group:android.permission-group.SENSORS
- permission:android.permission.BODY_SENSORS
- group:android.permission-group.LOCATION
- permission:android.permission.ACCESS_FINE_LOCATION
- permission:android.permission.ACCESS_COARSE_LOCATION
- group:android.permission-group.STORAGE
- permission:android.permission.READ_EXTERNAL_STORAGE
- permission:android.permission.WRITE_EXTERNAL_STORAGE
- group:android.permission-group.MICROPHONE
- permission:android.permission.RECORD_AUDIO
- group:android.permission-group.SMS
- permission:android.permission.READ_SMS
- permission:android.permission.RECEIVE_WAP_PUSH
- permission:android.permission.RECEIVE_MMS
- permission:android.permission.RECEIVE_SMS
- permission:android.permission.SEND_SMS
- permission:android.permission.READ_CELL_BROADCASTS
注意,通过上述的总结可以发现:dangerous permissions是分组的。分组对我们的权限机制有什么影响吗?
答案是有,如果app运行在Android 6.x的机器上,对于授权机制是这样的。如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。不过需要注意的是,不要对权限组过多的依赖,尽可能对每个危险权限都进行正常流程的申请,因为在后期的版本中这个权限组可能会产生变化。(引用自博文Android 6.0 运行时权限处理完全解析)
申请权限
声明权限
首先和以前一样我们需要在AndroidManifest文件中声明我们所需要的权限(无论是normal permissions还是Dangerous Permission)
<uses-permission android:name="android.permission.CALL_PHONE"/>
检查权限
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
}else{
//doSomeThings
}
代码解释:ContextCompat.checkSelfPermission
用于检测默哥权限是否已经授予,返回值是:PackageManager.PERMISSION_DENIED
或者PackageManager.PERMISSION_GRANTED
。前者表示还未授权,需要申请;后者表示已经授权,无需再申请。
申请权限
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
代码解释:该方法第一个参数是context;第二个参数是需要申请的权限字符串数组,可以看出在这里我们可以将多个需要申请的权限在这里一并申请了,系统会通过对话框逐一询问是否授权;第三个参数是request_code,用于在权限申请回调方法中区分申请。
处理权限申请回调
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限已经申请成功,在这里执行想要的权限操作
} else {
// 权限申请失败
}
return;
}
}
}
代码解释:首先通过在申请权限过程中设定的request_code来定位到你的申请,就是为了获取对应的grantedResult数组。grantedResult[]对应着requestPermissions()方法的第二个参数,权限数组。也就是说这两个数组的长度相同,一一对应:权限--申请结果(granted/denied)。最后你可以在申请成功后,执行一些针对权限的操作。
这里我们为什么用ContextComapt和ActivityCompat,就是为了向低版本兼容这样的特性。
权限解释
当用户在App触发第一次申请权限时拒绝申请后,再一次触发权限申请时,可以通过shouldShowRequestPermissionRationale()方法来向用户解释该权限的详细信息。
- 当App第一次安装,并且用户触发了权限申请,shouldShowRequestPermissionRationale()方法返回false
- 如果用户以前拒绝了一个请求,这个方法将返回true。那样的话你应该考虑在再次触发权限对话框之前显示一个解释请求用途之类的信息。你应该仅在权限本身意思不明了的情况下才做这件事情。
当App完全没有机会申请权限时,例如用户点击了不再询问等操作,表明再进行解释也是徒劳。此时shouldShowRequestPermissionRationale()方法返回false。
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);
}
}
实践
参考
目标
- 掌握自定义设置权限窗口,参考Android 6.0 - 动态权限管理的解决方案
- 引导用户设置,参考Android 6.0 - 动态权限管理的解决方案
- 如何进行权限解释
- Fragment的权限申请及回调