背景
刚开始工作,最近在看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...