英文原文:https://developer.android.com/training/permissions/requesting.html
从Android 6.0(API级别23)开始,用户在程序运行时给予权限,而不是当他们在安装应用程序时。这种方法简化了应用程序的安装过程,因为当他们安装或更新应用程序,用户无需授予权限。这也给了用户对于应用程序的功能更多的控制;例如,用户可以选择给予应用照相机的权限,但是不给予获得位置的权限。用户可以随时通过应用的设置界面来收回这些权限。
系统权限分为两类,正常的和危险的:
不危机用户隐私的权限。如果您的应用程序列出其正常的权限清单时,系统会自动授予的权限。
危险的权限,即可以给予应用获得用户隐私的权限。如果您的应用程序列出其正常的权限清单时,系统会自动授予权限。如果列出一个危险的权限,用户必须明确地给予您的应用程序此权限。
欲了解更多信息,请参阅 Normal and Dangerous Permissions.(需爬梯)
在Android的所有版本中,你的应用需要在App manifest 中声明所有普通权限与危险权限,即显示在安装时所显示的权限清单上。然而,该声明的效果对于不同的系统版本和不同的App Target SDK级别是不同的:
如果程序运行在Android5.1与更低版本上,或者应用的Target SDK小于等于22:如果您在您的清单中列出危险的权限时,用户必须在他们安装App的时候授予App这些权限,否则系统根本不会安装这个App。
如果程序运行在Android6.0与更高版本上,并且应用的Target SDK大于等于23:App同样需要列出所有所需权限,但是他在使用每一个危险权限时都需要用户的统一。用户可以授予或拒绝每一个权限,虽然用户拒绝了这些权限但是程序依然可以选择不使用此权限运行。
注意:从Android6.0(API级别23)开始,用户可以随时撤销任何应用程序的权限,即使该应用程序的Target API低于23。您应该测试您的应用程序,在应用程序缺少一个必要权限时也可以在无论什么级别的API上正常运行。
本文介绍如何使用Android Support Library检查,并请求权限。 Android框架提供了类似的方法在Android6.0(API等级23)。但是,使用支持库更加简单,因为你的App在调用方法前无需检查程序的运行在什么Android版本上。
如果你的应用程序需要一个危险的权限,必须在每次执行此权限前都要检查是否拥有此权限。因为用户可以随时撤销许可,所以即使昨天应用程序可以使用相机,但是今天不一定拥有权限来使用相机。
我们可以通过ContextCompat.checkSelfPermission()
方法来检查App是否拥有权限。
例如,下面这个例子展示了Activity是否拥有写入Calendar的权限:
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_CALENDAR);
如果应用已经获取了权限,该方法将返回PackageManager.PERMISSION
。理所当然的,应用程序可以继续执行操作。如果应用程序没有权限,该方法返回PERMISSION_DENIED
,则App需要明确的向用户请求此权限。
如果你的应用程序需要的是在应用程序清单中已经列出的一个危险的权限,则必须向用户请求授予此权限。您可以使用几种Android提供的方法来请求权限。调用这些方法会唤起一个标准的Android对话框(此对话框您不可以自定义)。
解释为什么应用程序需要此权限
在某些情况下,您可能希望帮助用户了解为什么你的App需要这些权限。例如,如果用户启动一个摄影应用,用户可能不会感到惊讶,应用程序要求使用摄像头的权限,但用户可能不理解为什么应用程序要访问用户的位置或联系人。在你请求这个权限时,您应考虑给用户提供一个很解释。请记住,你不想用解释压倒用户;如果你提供太多的解释,用户可能觉得应用程序很烦会直接删除应用。
另一种是当用户已经拒绝了这些权限时。如果用户不停地尝试使用需要权限的功能,但时程序始终无法获得权限,这可能表明该用户不理解为什么应用程序需要提供该功能的权限。在这样的情况下,它可能显示一个解释是一个好主意。
为了帮助用户获得这些解释,Android提供了一个工具方法,shouldShowRequestPermissionRationale()
。如果应用程序在之前已请求此权限,但是用户拒绝了这一要求时,此方法返回true
。
注意:如果用户之前已经拒绝许可请求,并在选择在许可请求的系统对话框中,选择了不要再问(Don't ask again)
选项,此方法返回false
。如果设备策略禁止获得此方法的话,此方法同样返回false
。
请求你所需要的权限
如果你的App并未拥有所需要的权限,那么App必须使用一个名为requestPermissions()
的方法来请求批准这些权限。
如果你的App通过了权限请求,并指定了用于识别这个请求的整数请求代码。这个方法是异步的:在用户相应权限请求对话框后,系统会调用App的回调方法连同之前的整数请求代码一起返回给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()之前提供,按照之前章节 解释为什么应用程序需要此权限 那样做。
处理权限请求响应
当你的应用程序请求权限时,系统会显示一个对话框给用户。当用户做出响应,系统将调用您的App的onRequestPermissionsResult()方法,传递给你用户的响应。您的应用可以复写onRequestPermissionsResult()方法来确认是否权限被授予。回调给你传递给requestPermissions()相同请求代码。例如,如果一个App请求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需要的访问权限组;但是它没有列出具体的权限。例如,如果你要求READ_CONTACTS
权限,系统对话框,只是说你的应用程序需要访问该设备的联系人。只需要为一个权限组授权一次就好。如果App请求其他属于这个权限组的权限时,系统会自动授予这些权限。当你请求权限时,并且用户同意请求,系统会调用你的onRequestPermissionsResult()
回调方法,并传递PERMISSION_GRANTED
。
注意:即使用户已经同意在同一权限组中的另一种权限,您的应用程序仍然需要明确地请求每次所需要的权限。此外,权限组的组成可能会在将来的Android版本中改变。你的代码不能假定一个权限一定在某个权限组中。
例如,假设你在App manifest中列出了 READ_CONTACTS
和WRITE_CONTACTS
权限。如果您请求了READ_CONTACTS
权限并且用户授予了该权限,然后你请求WRITE_CONTACTS
权限,系统可以无需与用户交互即可授予您该权限。
如果用户拒绝了权限要求,您的应用程序应该采取适当的行动。例如,你的应用程序可能会显示一个对话框,解释为什么它为什么不能执行用户所请求的操作,是因为需要该权限。
当系统要求用户授予权限,用户告知系统不要再问了该权限的选项。在这种情况下,应用程序使用requestPermissions()
不管什么时候再次要求该权限,系统都会立即拒绝请求。即系统调用您onRequestPermissionsResult()
回调方法,并传递PERMISSION_DENIED
,这相当于,用户曾明确会再次拒绝你的要求。这意味着,当你调用requestPermissions()
,你将不能与用户拥有任何交互。