Android 6.0 (API 23, M)以上对permission的变化

背景

刚开始工作,最近在看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 activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.WRITE_CALENDAR);

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

请求permission

如果应用没有获得某个permission,则可以通过调用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 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的结果是在这个方法中处理的

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

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

更多知识

permission group

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

充分测试

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

官方链接

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

你可能感兴趣的:(android,学习笔记,思考,解决问题)