当APP需要访问APP沙箱外部数据或资源的区域,则需要声明权限。APP必须在AndroidManiffest文件中,通过
声明所需的权限。例如APP需要网络连接,则在manifest中添加一行
"http://schemas.android.com/apk/res/android"
package="com.example.xxx">
"android.permission.INTERNET"/>
...>
...
如果应用在manifest中列出了正常权限,系统会自动授予APP这些权限。
如果应用在manifest中列出了危险权限,则需要用户明确同意授予这些权限。
Android权限分成几个保护级别,保护级别会影响是否需要APP运行时申请权限。
目前分为三个保护级别:Normal
、Sigature
、Dangerous
权限。
Normal权限在APP安装时系统自动授予。
Sigature权限也是在APP安装时系统授予。
Dangerous权限则需要在APP运行时,APP提示用户授予权限,即动态申请。
Normal权限不会直接给用户隐私权带来风险。如果APP在其manifest中声明Normal权限,则系统会在安装时自动授予APP该权限。系统不会提示用户授予Normal权限,用户也无法撤消这些权限。
从Android 8.1(API 27)开始,以下权限为Normal:
顾名思义,Sigature权限仅限于用该APK(定义了这个权限的APK)相同的私钥签名的应用才可以申请该权限。
从Android 8.1(API 27)开始,以下权限为Signature:
Dangerous权限分成与设备功能相关的权限组,便于用户在授予权限时做出更有意义和更明智的选择。
权限组 | 权限 |
---|---|
CALENDAR | READ_CALENDAR |
- | WRITE_CALENDAR |
CAMERA | CAMERA |
CONTACTS | CONTACTS |
- | READ_CONTACTS |
- | WRITE_CONTACTS |
- | GET_ACCOUNTS |
LOCATION | ACCESS_FINE_LOCATION |
- | ACCESS_COARSE_LOCATION |
MICROPHONE | RECORD_AUDIO |
PHONE | READ_PHONE_STATE |
- | READ_PHONE_NUMBERS |
- | CALL_PHONE |
- | ANSWER_PHONE_CALLS |
- | 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 |
运行时请求Dangerous权限
如果设备运行的是Android 6.0(API 23)或更高版本,并且APP的targetSdkVersion为23或更高,则在安装时,不会通知用户任何APP权限。APP必须在运行时提示用户授予Dangerous权限。
如果设备运行Android 5.1.1(API 22)或更低版本,或者targetSdkVersion为22或更低,则在安装时,系统会自动要求用户授予所有Dangerous权限。
例如:检查APP是否具备摄像头权限Manifest.permission.CAMERA为例,并根据需要请求该权限
// 检查权限,没有返回true,需要申请
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.CAMERA)) {
// 如果应用之前请求过此权限但用户拒绝了请求,此方法将返回true,这步判断非必须
// 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.CAMERA},
MY_PERMISSIONS_REQUEST_CAMERA);
// MY_PERMISSIONS_REQUEST_CAMERA is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
注:当APP调用requestPermissions()时,系统将向用户显示一个标准对话框。APP无法配置或更改此对话框。如果需要为用户提供任何信息或解释,应在调用requestPermissions()之前进行,如解释应用为什么需要权限中所述。
当应用请求权限时,系统将向用户显示一个对话框。当用户响应时,系统将调用应用的onRequestPermissionsResult()方法,向其传递用户响应。因此,需要在Activity或者Fragment实现onRequestPermissionsResult方法。
@TargetApi(Build.VERSION_CODES.M)
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_CAMERA: {
// If request is cancelled, the result arrays are empty.
// 异常情况,grantResultsw也是空数组
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
为了方便申请动态权限,可以使用一些开源库,比如RxPermissions,将权限申请的代码requestPermissions()和请求结果的代码onRequestPermissionsResult()放在一起管理,避免了代码的分散,同时具备Rx(RxJava)的特性。
使用如下
private void requestPremission() {
RxPermissions rxPermissions = new RxPermissions(this);
rxPermissions.request(Manifest.permission.CAMERA)
.subscribe(new Consumer() {
@Override
public void accept(Boolean granted) throws Exception {
if (granted) {
Log.d("zzhtest", "permission = " + granted);
} else {
Log.d("zzhtest", "permission = " + granted);
}
}
});
}
参考
Permissions overview(需)
Request App Permissions(需)
一行代码搞定Android 6.0动态权限申请
使用RxPermissions(基于RxJava2)