您的位置:首页 > 移动开发 > Android开发

Android 6.0 应用权限(二) -- 与系统权限一起工作(Working with System Permissions)

2016-05-10 00:00 501 查看
为了保护系统集成度和用户的隐私,Android在一个受限访问的沙箱中运行每个程序。如果这个应用想要使用它的沙箱外的其他资源或信息,就必须显性地请求权限。依赖于应用请求的权限类型,系统可能会自动地授权或者通过询问用户来授权
本节向你展示如何为你的应用声明和授予权限。

Declare Permissions(声明权限)

每个Android应用都运行在一个受限访问的沙箱中。。如果这个应用想要使用它的沙箱外的其他资源或信息,就需要请求正确的权限。你可以通过在App Manifest中列出这些权限来声明你的应用所需要的权限。
依赖于权限的敏感程度,系统可能自动地进行授权,也可能设备的用户要对权限的请求进行许可。例如,如果你的应用请求一个关闭设备闪光灯的权限,系统会自动地赋予它这个权限,但是,如果你的应用需要读取用户的联系人,系统会要求你准许这个权限。根据不用的平台版本,用户进行授权地方式也不是:在安装的时候授权(Android 5.1或更低)或在应用运行的时候(Android 6.0或更高)

决定你需要哪些权限

当你开发你的应用的时,你应该注意那些你的应用正在使用一些需要权限的功能。典型的场景是,无论什么时候一个应用将要使用那些不是它自身创建的信息或资源,或者执行一些会影响到设备或者其它应用的行为的动作,它都将会需要权限。例如,如果应用需要访问网络,使用设备的相机,或者打开或关闭WiFi, 它需要合适的权限。想要查看系统权限的列表,可以查看(Normal andDangerous Permissions 章节)
你的应用仅仅需要那些它会直接执行的动作的权限。如果你的应用请求其它应用执行任务或者提供数据,它并不需要权限。例如,如果你的应用需要读取这个用户的地址薄,这个应用需要 READ_CONTACTS 权限。 但是如果你的应用使用一个 intent 去向用户的 Contacts 应用请求信息, 你的应用不需要任何权限。 但是 Contacts 应用一定需要相应的权限。想要查阅更多信息, 请查看 Consider
Using an Intent 章节。

添加权限到应用清单(Add Permissions to the Manifest)

为了声明那些根据权限,要将<uses-permission>元素放进你的app manifest, 作为顶层<manifest>的子元素。例如,一个应用需要发送SMS消息需要如下代码来声明权限。
<manifestxmlns:android="http://schemas.android.com/apk/res/android"

package="com.example.snazzyapp">

<uses-permissionandroid:name="android.permission.SEND_SMS"/>

<application ...>

...

</application>

</manifest>
在你声明了一个权限后,系统的行为依赖于权限的敏感程度。如果这个权限不影响用户的隐私,系统会自动进行授权。如果这个权限可能会允许访问敏感的用户信息,系统会要求用户批准这个请求。

Requesting Permissions at Run Time

从Android 6.0开始(API level 23), 用户在应用运行时进行授权,而不是在安装应用的时候。这个方法(approach)简化(streamline)了应用的安装过程,因为用户并不需要在它们安装或者更新应用的时候进行授权。同时,它也给了用户对应用功能的更强的控制;例如,一位用户可以选择让一个相机应用去访问相机,但是不允许其访问设备的位置。 用户可以在任何时候通过应用的设置界面去撤回(revoke)这些权限。
系统权限(System Permissions)被 分为两类,普通的和危险的(normal and dangerous) :
· Normal Permissions 不直接承受用户隐私的风险。如果你的应用在它的manifest里面列出 了一个普通权限, 系统会自动地对它进行授权。
· Dangerous Permissions 会让应用访问这个用户的私密(confidential)数据。如果你的应用在它的manifest里面列出了一个普通权限, 系统会自动地对它进行授权。如果你列出的是一个危险权限,用户就必须显性地对你的应用进行授权。
在所有的Android 版本中,如同 Declaring Permissions所描述的, 你的应用需要同时在应用的manifest 中声明它需要的 normal 和dangerous 权限。但是,那种声明的具体效果会由于系统的版本和你的应用的目标sdk级别(targetSdkVersion)的差异而不同。
· 如果设备运行在Android 5.1或更低版本,或者你的应用 target SDK 是22或者更低, 如果你将一个危险权限列在你的应用清单中,用户必须在他们安装这个应用的时候进行授权,如果他们没有授权,系统根本不会安装这个应用。
· 如果你的设备运行在Android 6.0 (API level 23)或更高版本,你的应用必须在manifest中列出需要的权限,并且它必须在应用运行的时候请求每个它需要的危险权限。用户可以授权或者拒绝每一个权限,并且这个应用可以继续在受限的功能的状况下运行,即使用户拒绝了所请求的权限。
Note: 从 Android 6.0 开始,用户可以在任何时候撤消相应应用的权限, 即使这个应用的targetapi level 是较低等级的。你应该测试一下你的应用来验证它是否可以在它缺少所需要的权限的情况下正常工作,不论你的应用指向哪 个API级别的应用。
本节描述了如何使用 Android Support Library 来检查和请求权限。 Android 框架提供了类似的方法在Android 6.0上。 但是,使用 support library 更加简单,因为你的应用并不需要在调用 这个方法前检查它所在的 Android 版本

Check for Permissions

如果你的应用需要一个危险权限,你必须在每次执行需要那个权限的操作的时候检查你的应用是否已经具有这个权限。用户通常可以很方便地去撤销已经给予的权限,因此即使应用昨天用过相机权限,也不能想当然地认为它今天仍有这个权限。
为了检查你是否具有某个权限,调用ContextCompat.checkSelfPermission() 方法。举个例子, 下面这个片断(Snippet)向我们展示如何检查某个activity是否拥有写Calendar的权限。
// Assume thisActivity is the current activity

int permission Check
=ContextCompat.checkSelfPermission(thisActivity,

Manifest.permission.WRITE_CALENDAR);
如果这个应用有上面的权限,这个方法返回 PackageManager.PERMISSION_GRANTED,并且这个应用可以执行相应的操作。如果应用没有这个权限,这个方法返回 PERMISSION_DENIED ,并且应用必须显式地向用户请求权限。
Request Permissions(请求权限)
如果你的应用需要一个列出在你的app manifest中的危险权限,就必须要求用户对这个权限进行授权。 Android 提供了几种你可以用来申请一个权限的方式。调用这些方法会弹出一个你不能去定制的标准的安卓对话框。
Explain why the app needs permissions
在某些环境(circumstance)下, 你也许想要帮助用户去理解为什么你的应用需要某个权限,比如,如果一个用户启动了一个图片应用,这个用户可能不会对应用要求使用相机的权限感到惊讶,但用户可能不会理解为什么这个应用想要访问用户的位置或者联系人。在你请求一个权限前,你应该考虑给用户一个解释。如果你提供了太多解释,这个用户可能会觉得这个应用很烦就把它给卸掉了。
只有当用户已经拒绝了某个权限申请的时候你可能会用到一种方式来提供一个解释。如果用户一直试图使用那个需要权限的功能,但是一直拒绝这个权限申请,那可能表示用户不明白为什么应用在使用这个功能的时候需要这个权限。在那种场景下,显示一个解释内容给用户很可能是一个好主意。
为了帮助找到那些用户可能会需要一个解释的场景,Android提供了一个工具方法。 shouldShowRequestPermissionRationale().这个方法在应用之前已经请求过权限但用户拒绝的情况下返回
true 。
Note: 如果用户在过去拒绝了某个权限请求并且选择“Don't ask again” 选项,这个方法将返回false , 如果设备策略禁止应用具有那个权限,这个方法也会返回 false.
Request the permissionsyou need.
如果你的应用尚未拥有它需要的权限,这个应用必须调用 一个, int)]requestPermissions() 方法来请求合适的权限。你的应用传递它想要的权限和你定义这个权限请求的一个整型的请求码(request
code). 这个方法异步地(asynchronously)运行: 它会立刻返回,并且在用户响应这个对话框后,系统会回调应用的回调函数,并把结果和相同的应用传递过来的传给, int)]requestPermissions().的request
code传回来。
下面的代码检查应用是否具有读取联系人的权限,并且在必要的情况下请求这些权限。
// 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 theuser

// sees the explanation, try again to request thepermission.

}else{

// No explanation needed, we can request the permission.

ActivityCompat.requestPermissions(thisActivity,

newString[]{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.

}

}
注意: 当你的应用调用 , int)]requestPermissions()
时, 系统向用户显示一个标准的对话框,你的应用不能修改这个对话框,如果你需要向用户提供任何信息或者解释内容, 你应用在你调用 , int)]requestPermissions() 之前就那样做。正如上面所述.

处理权限请求的应答(Handle the permissions request response)

当你的应用请求权限时,系统向用户展示一个Dialog。当用户响应时,系统调用你的应用的, int[])]onRequestPermissionsResult()方法,把用户的响应传给它。你的用户必须重载这个方法来所需要的权限是否被授予。系统会把你传递给 , int)]requestPermissions() 的相同的请求码传给这个回调函数。例如,
如果一个应用请求 READ_CONTACTS 访问,它也许会有下列的回调方法:
@Override

public void onRequestPermissionsResult(int
requestCode,

String permissions[],int[]
grantResults){

switch(requestCode){

case MY_PERMISSIONS_REQUEST_READ_CONTACTS:{

// If request iscancelled, the result arrays are empty.

if(grantResults.length
>0

&& grantResults[0]==PackageManager.PERMISSION_GRANTED){

// permission wasgranted, yay! Do the

// contacts-relatedtask you need to do.

}else{

// permission denied,boo! Disable the

// functionality thatdepends on this permission.

}

return;

}

// other 'case' lines to check for other

// permissions this app might request

}

}
这个系统显示的对话框描述了你的应用需要访问的权限组;它并不会列出某个特定的权限。例如,如果你请求 READ_CONTACTS 权限,系统对话框仅会说你的应用需要访问设备的联系人。用户仅需要对每个权限组授权一次。如果你的应用请求了那个权限组中的任意一个其它的权限(你列在你的应用清单中的权限),
系统会自动对这个权限进行授权。当你请求这个权限时,系统会调用应用的, int[])]onRequestPermissionsResult() 回调方法并且传递 PERMISSION_GRANTED
提示:你的应用仍需要显性地申请每一个它需要的权限,即使用户已经授权另一个在相同权限组的权限。而且,划分的权限组在将来的安卓release中可能会发生变化。你的代码应该依赖特定的在或不在相同权限组进行假设。
例如, 假设你在你的app manifest中列出了READ_CONTACTSWRITE_CONTACTS 两个权限,如果你请求 READ_CONTACTS
,用户授予了这个权限,然后你请求 WRITE_CONTACTS , 系统会不经过与用户交互就授予你这个权限。
如果用户拒绝了你的权限请求,你的应用需要采取正确的行动。例如,你的应用也许需要显示一个对话框来解释为什么它不能执行用户请求的需要那个权限的动作。
当系统要求用户授予一个权限时,用户可以看到一个选项,勾选后会告诉系统下次不再询问权限。如果勾选了这个权限,当应用再次使用 , int)]requestPermissions() 来请求权限时,
系统会不经过询问直接拒绝用户。系统会调用应用的, int[])]onRequestPermissionsResult() 回调方法,并把 PERMISSION_DENIED 传递给它。具体形式与用户点击扎约权限请求时的表现一样。这意味着当你调用 , int)]requestPermissions() 时,你不能假设发生任何形式的与用户的直接交互。

Permissions Best Practice

对应用而言很容易通过权限请求来打击到一个用户。如果用户发现一个应用用起来很烦,或者用户担心这个应用会对自己的信息做些什么,他们也许会直接不再使用这个应用或者干脆将它删除掉。下面这些最佳实践可以帮助避免这些不好的用户体验。

Consider Using an Intent

在很多情形里,在你的应用里面为了执行一个任务,你有两种选择。你可以让你的应用请求权限来执行对应的操作。当然,你也可以选择让你的应用使用 intent 来让其它应用执行这个任务。
例如,假设你的应用需要使用设备的相机拍照,你的应用可以请求 CAMERA 权限(允许你的应用直接访问相机),你的应用将使用camer
API来控制相机并且拍照。这个方法可以给你的应用完整的对相片进程的控制,并且让你 将相机UI合并到你的应用中。
然而,如果你不需要这种完全的控制,你可以使用一个 ACTION_IMAGE_CAPTURE intent来请求一个相片。当你发送这个intent时,
系统提示用户选择一个相机应用(如果没有默认的相机应用)。用户使用选定的相机来拍照,并且那个应用把得到的相片返回到你的应用的 onActivityResult() 方法。
类似地,如果你需要打一个电话,访问用户的联系人,等等,你可以通过创建一个合适的intent来做这些事情。或者你可以请求对应的权限来直接访问合适的对象。每种方法都有其优缺点:
如果你使用权限:
· 在你执行一个操作的时候,你的应用具有对用户体验的完全的控制。但是,这种宽泛的控制增加了你的任务的复杂性,因为你需要设计一个合适的UI。
· 用户会被提示一次权限请求,不管是在运行的时候还是在安装的时候(依赖于用户的Android 版本)。之后,你的应用可以在不需要与用户产生额外的交互的情况下执行操作。但是,如果用户没有给予这些权限(或者在随后取消了),你的应用将不再能执行这些操作。
如果你使用intent:
· 你不需要为这个操作设计UI. 那些处理intent的应用提供了UI.但是, 这意味着你不能够控制所有的用户体验。用户可能会与一个你从来没有见过的UI产生交互。
· 如果用户没有一个默认应用,系统提示用户选择一个应用。如果用户没有指定一个默认的处理者,他们将会在每次执行这个动作的时候产生一个额外的对话框。

Only Ask for Permissions You Need

每次,在你请求一个权限的时候,你都在强迫用户做决定。你应用使你发出这些请求的次数尽可能小。如畅想用户是在Android 6.0 或者更高的版本上,用户每次尝试使用一些新的需要权限的应用特性, 应用都必须用一个权限请求来打断用户的工作。如果用户是在更早一些的版本上运行,用户就必须在应用安装的时候进行授权。如果这个列表太长或者不够准确,用户可能会决定不安装你的应用。对于这些理由,你应该尽可能地减少你的应用需要的权限。
通常你的应用可以通过使用intent 避免申请权限.如果一个特性不是你的应用功能的核心部分,你应用考虑通过其它的程序来处理这个任务。

Don't Overwhelm the User

如果用户设备运行在Android 6.0或者更新的版本,用户必须在他们使用这个应用的时候对你的应用进行授权。一旦你让用户面临大量的权限请求的情况,你可以会打击到他们使用这个应用的信心,使他们卸载掉这个应用。相机,你应该只在你需要他们的时候进行授权。
在某些情况下,一个或多个权限可能对你的应用而言是必备的。在应用一启动就请求所有这些权限可能是有意义的。例如,如果你写了一个图片应用,这个应用将需要访问设备的相机,当用户第一次启动这个应用,他们不会对请求相机权限的操作感到惊讶。但是如果相同的应用同时也具有通过用户的联系人分享图片的需求,你可能不应该在首次登录的时候请求 READ_CONTACTS 权限。相反,应该等到用户尝试去使用“分享”功能并且请求权限的时候再弹出提醒。
如果你的应用提供了一个教程,在教程的最后去请求应用的必备的权限很有意义。

Explain Why You Need Permissions

当你调用, int)]requestPermissions() 时由系统显示的权限对话框会告诉我们你的应用想要什么权限,但是并没有说为什么、在某些情形里,用户可能会发现这个迷惑点。在调用, int)]requestPermissions()前 向用户解释为什么你的应用想要这些权限是个好主意。
例如,一个图片应用可能想要使用位置服务,这样它就可以给图片打上位置信息。一个典型的用户可能不理解为什么一个图片要包括位置信息,并且会对为什么一个图片应用想要知道位置信息感到迷惑。所以在这种情形里,在调用 , int)]requestPermissions() 前告诉用户这个特性是个很好的主意。
一个通知用户的方式是将这些请求组合成一个应用教程,这个教程可以按顺序去逐个展示应用的特性,并且在展示的时候解释它们需要哪些权限,例如,图片应用教程可以展示它“向你的联系人分享图片”的特性。然后,就可以告诉用户他们需要给应用查看用户的联系人的权限。然后,应用就可以去调用 , int)]requestPermissions() 来向用户请求权限,当然,并不是每个用户都会跟随教程,因此,你仍然需要在用户的普通操作里检查和请求权限。

Test for Both Permissions Models

从安卓6.0开始,用户可以在应用运行时授权或撤销权限,而不是像以前那样在应用安装的时候去检查。因此,你必须在更宽泛的条件下测试你的应用。到Android6.0之前,你可以合理地假设您的应用程序运行时具有所有它的应用程序清单中声明的权限。但根据新的权限模型,你不能再做这样的假设。下面的建议将帮识别运行在API leve 23或更高的设备上的权限相关的代码问题:
· 识别你的应用的当前的权限和相关的代码路径
· 测试用户跨权限保护的服务或数据
· 测试多种地授权和非授权地权限组合。例如,一个相机应用可能在manifest中列出了CAMERA, READ_CONTACTS,
ACCESS_FINE_LOCATION 。 你应该用这些权限里面的每个权限来测试这个应用,打开关闭里面的一个或多个权限,确保应用可以处理所有的权限配置。记住,从Android6.0开始,用户可以关闭任何应用的权限,即使那个应用的目标APIlevel是22或者更低。
· 使用adb工具来从命令行管理权限:
List permissions and status by group:

$ adb shell pm list permissions -d -g

Grant or revoke one or more permissions:

$ adb shell pm [grant|revoke] <permission-name> ...
· 为那些使用权限的服务来分析的应用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: