在Android 6.0(API level 23)之后,用户是在应用运行时才去授予权限给应用,而不是在用户安装app的时候。这种方法简化了应用的安装过程,因为用户无需在应用安装或升级时去授予权限给应用。这也可以让用户更有效地监控应用的功能。例如,用户可以选择给相机应用一个访问相机的权限,而不给相机应用指定定位的权限。去到应用的设置界面,用户是可以随时撤销这个权限的。
系统权限被分为两类:normal 和 dangerous:
1>
normal 权限不会直接涉及用户的隐私。如果你的应用在manifest中列出这个normal权限,那么系统会自动同意这个权限。
2>
dangerous权限能够让应用去访问用户的隐私数据。如果你的应用在manifest中列出了一个normal权限,系统会自动同意这个权限,如果你在manifest中列出了一个dangerous权限,则需要征求用户的批准。
更多信息可以参考 Normal and Dangerous Permissions.
在所有版本的Android中,normal和dangerous都需要在应用的manifest中去声明,正如 Declaring Permissions所描述
的一样。但是,这种声明所带来的影响是不一样的,它取决于系统的版本和app的target SDK level:
1>
如果设备运行在 Android 5.1 或者 更低的版本上,或者你的app的 target SDK 是 22 或更低:如果你在 manifest 中列出了一个 dangerous 权限,在应用安装的时候,用户就必须同意这个权限;若不同意这个权限,则系统根本就不会去安装这个应用。
2>
如果你的设备运行在 Android 6.0 或者更高的版本上,并且,你的 app 的 target SDK 是 23或更高:app 也需要在 manifest中去列出这个权限,然后在app运行期间,对于dangerous的权限,应用在需要用到它时,应用必须对这个dangerous权限进行请求。用户可以同意这个权限,也可以拒绝这个权限,在这个权限被拒绝的时候,这个app依然可以运行,只是只能运行部分功能。
(译者备注:若在这个dangerous权限被拒绝了,那么与这个dangerous权限相关联的功能则无法运行)
须知:
在Android 6.0 (API level 23)以后,用户可以在任何时间撤销任何app的权限,即使这个app的target SDK level 低于 23。无论你app的 target SDK level是多少,当某个需要的权限被撤销时,你都应该测试一下你的app以检验其是否能正常工作。
这个章节主要讲述如何使用Android Support Library 去检查,请求权限。这个Android 框架提供了和Android 6.0
(API level 23)相似的方法。但是,使用 支持库更简单一些,因为在调用方法之前,你的应用不需要去检查所
运行的Android系统的版本。
如果你的应用需要一个dangerous权限,那么在每一次执行需要这个权限的操作时,你必须检查是否有这个权限。
用户总是可以任意地撤销这个权限的,所以即使你的应用昨天使用了摄像这个权限,并不能认为今天依旧有这个
权限。为了检查是否有这个权限,需要调用ContextCompat.checkSelfPermission()
方法。例如:下边这个片段展示
了如何检查这个activity上是否有写日历的权限:
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_CALENDAR);
如果你的应用有这个权限,这个方法就会返回 PackageManager.PERMISSION_GRANTED
,这个应用也可以继续
去执行操作。如果这个应用没有这个权限,这个方法就会返回 PERMISSION_DENIED
,
然后这个应用必须向用户
去请求这个权限。
如果应用需要一个在manifest中声明的dangerous权限,那么必须征求用户的同意。Android提供了几种方法供你去请求权限。调用这些方法会弹出一个标准的Android对话框,这个对话框你是没法自定义的。
图1 . 系统对话框提示用户同意或拒绝一个权限
在某些情况下,你也许想要帮助用户去理解为什么你的app需要这个权限。例如,如果一个用户启动了摄像的app,
用户并不会因应用请求一个使用照相机的权限而感到惊讶,但是用户可能不理解的是:应用为什么还想要去访问
用户的位置信息及联系人。在你请求权限之前,你应该向用户解释一下为什么要请求这个权限。记住,在解释的
时候,你不应该强迫用户去接受。如果你解释得太多,用户也可能会因此而感动沮丧,然后将其卸载掉。
一种你可能用得到的方法是,只有当用户已经拒绝了这个权限请求,你才提供相应的解释。如果用户想要去使用
这个功能,而这个功能需要权限,但用户每次却都拒绝了这个权限请求,那么有可能表明用户不理解为什么提供
这个功能需要权限。在这种情况下,可以给用户解释去解释一下。
为了帮助解决用户什么情况下才可能需要解释,Android提供了一个shouldShowRequestPermissionRationale()
方法。如果一个应用之前请求过这个权限,然后用户给拒绝了,那么这个方法就会返回 true 。
须知:
如果之前用户拒绝了这个权限,且在权限请求的系统对话框中勾选了 Don't ask again 选项,那么这个方法
就会返回 false 。如果一个设备的机制不允许你的app有这个权限,那么此时这个方法也会返回 false.
如果一个应用不具备它所想要的一个权限,那么应用就必须调用其中一个requestPermissions()方法去请求一个合适
的权限。你应用应该向这个方法里边传入一个它所想要的权限,和一个整型的请求码。这是一个异步的方法:在用户用户对对话框作出响应之后,它的结果就立刻被返回。
下面这段代码是用来检验这个app是否有取出用户联系人的权限,如有必要,再去请求权限:
// Here, thisActivity is the current activity
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);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
须知:
当你的应用调用 requestPermissions()
,系统会显示一个标准的对话框给用户,你的应用是没办法配置和修改这个
对话框的。如果你想要提供一些信息或解释给用户,你应该在调用 requestPermissions()
之前去做这件事,如
Explain why the app needs permissions 所述。
处理权限请求的响应
当你的应用请求权限的时候,系统弹出一个对话框给用户。当用户做出响应的时候,系统会调用应用的
onRequestPermissionsResult()
方法。你的应用必须重写这个方法,用以判断这个权限是否被同意。这个回调
方法传递的请求码和 requestPermissions()
传入的请求码是一样的。举个例子,如果你的应用请求了个 READ_CONTACTS
访问,它的回调函数也许如下:
@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) {
// 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
}
}
这个对话框被系统所显示出来,其描述了你app所需要的permission group ;它并没有列出某个具体的权限。
举个例子,如果你请求了一个READ_CONTACTS 权限,然后系统对话框仅仅只会说你应用需要访问设备的联系人。
对于每一个权限组来说,用户只需同意权限一次即可。如果你的应用请求这个权限组的其他权限(这些权限在manifest已有声明),系统会自动同意它们。当你请求这个权限,系统调用onRequestPermissionsResult()
这个回调函数,
然后其传入的参数为 PERMISSION_GRANTED。
须知:
尽管用户同意了同一个权限组的其他权限,你的应用依然需要对其所需的权限进行一个请求。此外,这个权限组在将来Android的发布版本中,可能会有些变化。你的代码不应该依赖某个权限是否在同一个权限组中的这种假设。
例如,假设你同时在 manifest中列出了READ_CONTACTS
和 WRITE_CONTACTS
。如果你请求了,然后用户同意了这
个权限,你然后再去请求 WRITE_CONTACTS
,系统就会立刻同意 WRITE_CONTACTS
这个权限,而不会再去征求用户。
如果用户拒绝了这个权限请求,你的应用就应该采取对应的一些措施。例如,你的应用可以显示一个对话框用来向用户解释为什么用户请求的需要权限的这个行为不能执行。
当系统询问用户去同意一个权限时,若用户勾选了不要再次问询这个权限的选项。在这种情况下,应用使用requestPermissions()
再次询问这个权限时,系统会立刻拒绝这个请求。系统调用 onRequestPermissionsResult()
回调函数,并传入 PERMISSION_DENIED 参数,这就意味着此时不会和用户有一个直接的交互。
关注微信公众号获取更多资讯