背景

刚开始工作,最近在看Android经典入门书籍《第一行代码》。后来才发现看的是第一版,由于Android版本升级,各种feature变了很多,所以书里有些例子已经不能在新一点的版本上正确运行了,我就踩到了一个坑,是关于Android 6.0以后系统对于应用permission的处理的变化。

踩坑过程

在《第一行代码》“8.2.1 接收短信”这一节里,书中给了一个demo项目,主要功能是一旦手机接收到短信的时候,app自动显示出发信人和短信内容。原理很简单:系统在收到短信的时候,会发出值为“android.provider.Telephony.SMS_RECEIVED”的一条广播,所以只需在代码里注册一个BroadcaseReceiver接收这个广播并且读出短信信息然后显示到屏幕上就行了。

高高兴兴的照着书敲完代码,在模拟器上运行程序,打开DDMS编辑模拟短息,send~走你~duang~~短信接收到了,但是应用上什么都没显示,立马傻眼。检查了一遍代码,确认没错,又尝试一次,duang~还是不行。于是开始怀疑是不是DDMS有什么问题,就用自己的手机(Android 5.0)试了一下,结果程序正常了,应用成功的显示了短信发信人和内容。这证明模拟器上确实是有什么地方不对劲,看了一眼Virtual Device Manager,我用的模拟器是Android 7.0,联想到之前Android 7.0上下载和打开文件所遇到的坑,警惕的怀疑是不是由于版本变化所引起的不同。于是在Android 5.0的模拟器上又尝试的一下,duang~成功了。这就证明了确实是版本升级所引起的不同。

填坑过程

确定了问题原因,开始搜索资料,经过一番调查,其实Android官方文档对permission的行为写的很清楚,在这里总结记录一下。

Android把permission分为两种:

  • normal permission:不会直接威胁用户的隐私

  • danger permission:可能会访问用户的隐私数据

应用要在Manifest文件中声明自己所需要的permission,然而用户在安装应用的时候,系统版本不同,则会有不同的对permission的处理:

  • 对于Android 5.1 (API 22)或更低版本的系统(以下简称“低版本系统”),在用户安装应用的时候,系统会展示应用所需的所有permission的列表(包括normal permission和danger permission),并询问用户是否同意授予所有权限。如果用户因为不想授予某一或某些权限,应用则不会被安装。

  • 对于Android 6.0 (API 23)或者更高版本(以下简称“高版本系统”),在用户安装应用的时候,系统会自动授予应用所需的所有normal permission并安装程序,但是并不授予应用所需的danger permission。用户有机会在应用运行时选择是否授予所有或部分danger permission,并且可以随时在设置里收回授予给应用的任何danger permission。

看到这里区别就比较明了了,对于低版本系统,如果不想授予某些权限,则应用根本不会被安装。对于高版本系统,如果你不想授予某些danger permission,只要选择不授予就可以了,不会影响应用安装,也不影响应用运行,但是某些feature可能就不能用了(比如你不想授予读取短信功能,那么应用里与短信相关的feature就不能用了,但是与短信无关的feature可以使用)。这样有一个好处就是用户可以控制不授予应用哪些数据,而即使不授予应用某些数据,用户仍然可以使用“残血版”的应用。

请求permission

高版本系统在运行时提供给用户授予permission的机会,所以开发者要在代码中对检查和请求permission做相应的处理,在Android framework里提供了相应的方法,在Support Library里也提供了类似的方法。Android的官方文档推荐使用Support Library里的方法,因为使用更简单。

检查permission

可以使用ContextCompat.checkSelfPermission()来检查是否已经获得了某个permission:

// Assume thisActivity is the current activityint permissionCheck = ContextCompat.checkSelfPermission(thisActivity,        Manifest.permission.WRITE_CALENDAR);

如果应用已经获取了permission,则返回PackageManager.PERMISSION_GRANTED,如果没有获取,则返回PERMISSION_DENIED。

请求permission

如果应用没有获得某个permission,则可以通过调用requestPermissions()来请求权限

// Here, thisActivity is the current activityif (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 explanation 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()之前先调用了一个方法shouldShowRequestPermissionRationale(),这个方法给应用一个机会向用户解释为什么要请求这个permission。

当requestPermissions()这个方法被调用时,系统会弹出一个样式固定的弹窗,这个弹窗不能定制,供用户选择是否授予permission。requestPermissions()是异步执行的,这个方法会马上返回。在用户做出选择之后,系统会调用onRequestPermissionsResult(),对请求permission的结果是在这个方法中处理的

@Overridepublic 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    }}

看到这,其实我的问题已经有了解决方法,先检查是否有读取短信权限,如果有的话直接注册广播接收器,如果没有的话请求权限,如果获得权限之后再注册广播接收器,运行,duang~成功~

更多知识

permission group

Android的permission有个权限组(permission group的概念),比如RECEIVE_SMS、READ_SMS、SEND_SMS......都属于SMS组,在应用已经获得了某一组中的某一权限,则下次再请求同组中的其他权限时,系统不会询问用户则直接授予请求的权限。

充分测试

官方文档提醒开发者,由于用户可能不授予请求的permission,所有开发者要充分测试各种情况,确保在有没有权限的时候应用要有合理的表现。

官方链接

对于记录的不是很详细的地方,可以参考官方链接:https://developer.android.com...

更多相关文章

  1. Android系统图标设计原则
  2. 从系统角度理解Android的界面绘制
  3. 关于“Android SDK manager中不出现完整Android版本安装包列表”
  4. 波音787 Dreamliner机舱娱乐系统全面拥抱Android
  5. [置顶] android用户输入系统详细说明
  6. windows系统上安装与使用Android NDK r5【2】
  7. Android关于apk版本更新方法

随机推荐

  1. Android内存泄露利器(hprof篇)
  2. Android命令行工具logcat详细用法!
  3. android Matrix原理
  4. Android studio如何导入Eclispe项目以及E
  5. Android发现局域网IP
  6. Android(安卓)7.1 预置GMS包
  7. Android(安卓)MapView 申请apiKey
  8. Android(安卓)对话框中的进度条 Progress
  9. Android 框架层为IMountService 增加新接
  10. Android下junit单元测试、logCat的使用