阅读谷歌官方教程第一章 Getting Started
关于权限,原先我在项目里面使用权限,只是在manifest.xml文件里,声明我需要的权限就好了,今天才知道,原来对一个应用来说权限的请求的重要性,更重要的是请求权限的方式,如果不能考虑用户感受,将会造成很可怕的后果。
这次我学到了,权限关于Android版本的细节,如何礼貌性地在系统运行的时候去申请权限,以及对于合理地声明权限的一些实践范例。
Working with System Permissions
To protect the system’s integrity and the user’s privacy, Android runs each app in a limited access sandbox. If the app wants to use resources or information outside of its sandbox, the app has to explicitly request permission. Depending on the type of permission the app requests, the system may grant the permission automatically, or the system may ask the user to grant the permission.
为了保护系统的完整性和用户隐私,Android会把每个APP运行在有访问限制的沙箱里,如果APP需要使用沙箱之外的资源或信息时,这个APP必须去请求权限。根据APP请求权限的类型,系统可能会默许,或是询问用户是否接受。
也就是说我们不一定声明了需要的权限,系统就会答应,也要看哪种类型的权限,比如打电话可能就行,查看联系人这样可能涉及用户私人信息的权限,就要问问用户了。
这次有三个内容:
- 声明权限
- 在运行时声明权限
- 声明权限的最好范例
没错,权限不单单是manifest.xml文件里的简单声明,下面开始吧。
第一个内容:Declaring Permissions
我们之前说系统会根据权限的敏感程度去决定是自动答应权限请求,或是询问用户。不过根据设备的系统版本,权限的请求也不同。在Android系统在5.1或以下的设备上运行APP时,应用会在一开始的时候就向系统请求全部它需要的权限,而在系统版本在6.0或以上的设备上运行时,是在运行时来请求权限。
1.Determine What Permissions Your App Needs
我们什么时候需要权限呢?
权限不能随意请求,只有我们需要的时候请求。如果应用需要一些信息,而那些信息或资源又不能自己创造出来,我们就需要权限。还有一种情况是我们需要改变设备的行为,比如开关WIFI。
我们只需要对应用功能有直接影响的权限。如果你的APP只是需要其他应用提供信息或是执行任务,就不必了。我们可以直接Intent启动其他应用并接受接受就可以了。比如说你的APP需要读取联系人的权限,我们可以直接在 Manifest.xml文件里声明,也可以不声明但是用Intent启动联系人应用并接受结果。这是一个选择,关于如果选择,下面会讲到。
2.Add Permissions to the Manifest
声明权限相信大家都知道,只需要在APP manifest文件的< manifest >下的子元素 < uses-permission >元素下面声明你需要的权限就行了。
官方给出了一个声明需要发送SMS信息的示例:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.snazzyapp">
<uses-permission android:name="android.permission.SEND_SMS"/>
<application ...>
...
</application>
</manifest>
最后提个醒,关于权限是否询问用户,由权限的敏感程度而定,如果涉及用户私人信息,系统会去询问用户是否准许。
第二个内容:Requesting Permissions at Run Time
先说明一点,这个功能只有在Android6.0及以上的设备上才行。
在Android6.0版本后(包括6.0),用户只会在APP运行时收到请求权限的申请,而不是在安装APP的时候。这个设定简化了安装流程,同时也给了用户对APP功能的更大的控制权利。比如说,用户可以给一个相机APP访问相机的权限,但是可以拒绝地理位置的权限。而且即使同意了,用户也可以在系统的权限设置页面随时取消应用权限。
系统权限可以分为两类,正常的和危险的:
- 正常的权限不会涉及用户的隐私,一般系统会自动允许,而无需告知用户。
- 危险的权限反之。
对于所有的Android版本,你都需要把APP需要的正常或危险权限声明在APP 的 Manifest文件里。而声明的影响却因为设备的Android版本和你的APP的target SDK等级而各不相同:
- 如果设备的Android版本在5.1及以下,或是你的target SDK 等级在22及以下,用户在安装APP的时候必须一项一项的决定是否准许Manifest文件里的危险权限,如果用户不同意,系统不会安装APP
- 如果Android版本在6.0及以上,SDK等级在23及更高,APP只会在运行时需要权限的时候向用户发起请求,当然还是对危险的权限发起请求,如果用户不同意,也会有限制APP功能的影响。
下面讲的都是用Android 自己的 Support Library 去检查和请求权限,虽然在6.0版本也提供了类似的方法,但是用Support Library 更简单,因为不需要检查设备的系统版本。
1.Check For Permissions
如果你的APP需要一项危险的权限,你每次运行涉及该权限的功能的时候都必须要检查是否拥有该权限。因为用户可以随心所欲的撤销应用的权限。比如说你的应用昨天可以使用相机,但你不能认为今天访问相机的权限还在。
为了检查是否拥有权限,我们应该调用 ContextCompat.checkSelfPermission() 方法。官方给出了检查是否当前Activity有向日历应用写东西的权限示例:
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_CALENDAR);
如果APP有权限,这个方法会返回PackageManager.PERMISSION_GRANTED,否则返回PERMISSION_DENIED,这时你必须显式地向用户请求权限。
2.Request Permissions
2.1 Explain why the app needs permissions
有时,我们应该要向用户解释为什么APP需要某项权限。比如对于一个相机应用来说,用户可能对于需要访问相机的权限觉得正常,但他可能不理解为什么需要有地理位置和联系人的权限。所以,在你向用户发起请求的时候,也许应该考虑先向用户解释为什么需要的这个权限。但是,也要注意,如果你解释的太多,可能会引起用户对你的APP的消极情绪。
我们什么时候需要向用户解释呢?一般就是这个情景,用户经常使用需要某项权限的功能,但是每次发起权限请求的时候都被拒绝,这个时候,向用户解释需要权限的原因是个很好的主意。
Android也提供了找出这个情景的方法shouldShowRequestPermissionRationale()。这个方法会返回true,如果APP之前请求过某一权限,但是被用户拒绝。如果用户在处理请求权限的时候选择了不要询问我(如下图所示),或者是设备本身不允许存在某一权限时,这个方法会返回 false。
2.2 Request the permissions you need
如果APP缺少必须的权限,这是就要调用requestPermissions()方法去请求权限。这个方法需要几个必须的参数,
- 你需要的权限
- 权限请求的整数码
这个方法是异步执行的。意思是,当用户响应请求权限的对话框后,它会立即返回结果。系统通过APP的回调方法来把结果返回,并把相同的整数码传回requestPermissions()。
下面展示了一个检查权限,确定是否需要解释和发起请求的示例:
// 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()去请求权限,系统会以对话框的形式向用户请求,这个对话框的样式我们不能定制或改变。如果你想向用户传递更多的解释信息,应该在调用requestPermission()这个方法之前向用户进行说明。
2.3 Handle the permissions request response
当你的应用请求权限的时候,系统会向用户发送对话框。一旦用户相应,系统就是自动回调APP的onRequestPermissionsResult()方法,并把结果传递给它,所以为了确定权限是否请求成功,我们必须覆写这个方法。这个回调方法会把你向requestPermission()方法中传递的整数请求码再传递回来。
官方给出了一个读取联系*限的请求结果返回示例:
@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需要读取联系人和修改联系人这两项权限,你向用户发起请求的时候对话框只会显示“XX应用需要访问你的联系人”,只要用户准许,系统就会自动允许你这个权限组里的其他权限申请。这时系统会回调onRequestPermissionsResult()方法,并传递PERMISSION_GRANTED,就和用户准许请求是一个效果
但是,如果用户拒绝了,你应该向用户简要的说明这些权限对于应用某些功能的重要性。
如果用户在拒绝的时候还勾选上了不要再询问我,那么以后APP再发起requestPermissions()去请求权限的时候,系统会立刻否定你的请求。系统会调用onRequestPermissionsResult(),并传递PERMISSION_DENIED,相当于用户显式地拒绝了请求。
第三个内容:Permissions Best Practices
1.Consider Using an Intent
最开始的时候就说了,使用权限时因为APP需要APP不能提供的信息或操作。但是有两种途径解决,一种是申请权限,另一种是用Intent启动其他APP的组件来解决问题。
打个比方,如果你的APP需要用设备的相机拍张照,这个时候你可以申请CAMERA权限,如果准许了,你就获得了控制相机应用的权限,你通过调用相机的API然后拍照,将结果返回。这个方法给了你拍照的充分控制权,但是你也需要将把相机的用户界面UI整合到APP里。
换言之,如果你并不需要这么完全的控制,你可以用ACTION_IMAGE_CAPTURE intent去请求一张相片,系统会筛选出能满足Intent操作和数据的APP来完成操作,用户选择一个相机APP执行操作(如果之前没有选择默认APP),然后结果通过onActiviyResult()把结果放回给你的APP。
官方给我们罗列了一下这两种方法的优缺点:
使用 Permissions:
- 可完全控制用户操作,但需要考虑整合UI的工作,增加任务的复杂度。
- 在运行时或是安装时,用户会对Permission请求给出响应,如果成功,那么以后执行操作就不需要再询问,如果失败,就完全不能进行相关需要改权限的操作了。
使用Intent:
- 不需要提供UI,但也不能完全控制操作,因为用户在另一个APP的操作你看不到。
- 如果用户没有默认的APP去处理相关操作,那么用户不得不每次都进行选择。
2.Only Ask for Permissions You Need
每次需要权限的时候,用户都不得不进行选择,不得不说,如果次数很多,肯定会造成用户的方案。如果设备Android版本在6.0及以上,每次需要尝试新功能,用户都要处理请求权限的事,它会打断用户的工作,引起不好的用户体验。在老版本设备,如果安装APP时,就一下面对一大串的权限,肯定是错误的。所以,我们应该尽可能的少用权限。
如果你的APP需要的权限太多,可以考虑使用Intent,在某项操作不是你的APP的核心时,可以考虑这个方法。
3.Don’t Overwhelm the User
不要让用户有被压制的感觉,比如安装APP时,一下子要同意一大堆权限。
也许某些权限对你的APP确实有很重要的作用,比如一个相机APP,必须要CAMERA的权限,用户打开的时候允许也能接受,但是不要把读取联系人,请求定位。。什么都一次性抛出来,应该在使用相关功能的时候再请求权限,比如当用户使用Share功能的时候,再请求读取联系人的权限。
4.Explain Why You Need Permissions
当你调用requestPermission()方法的时候,系统会显示请求权限的对话框,但是并不会显示为什么需要这个权限,所以用户可能会感到疑惑,这个时候应该在显示对话框之前向用户解释一下为什么需要这个权限。
比如说,一个相机APP可能想要地理位置来给照片打一个位置标签,但是没接触过的用户可能会对请求位置信息的权限不理解,所以我们最好在请求之前向用户解释一下why。
好了,今天的内容就是这么多了,看来应用权限,不但是自己的权限,更多是为了用户的权限。