这是一个关于targetSdkVersion的教训。
之前有用户反馈,在android12设备上,会报蓝牙权限适配导致的crash:
Caused by: Java.lang.SerurityException: Need android.permission.BlUETOOTH_CONNECT permission for android.content.AttributionSource@70c2370b: getName
看了下,Android 12上将原来的android.permission.BLUETOOTH
权限拆分成了三个:
BLUETOOTH_SCAN
、BLUETOOTH_ADVERTISE
和 BLUETOOTH_CONNECT
。
看了下适配文档,将SDK的AndroidManifest
里的蓝牙权限做了如下修改:
- <uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH"
+ android:maxSdkVersion="30" />
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
然后,Java代码里做了些相关改动。正好办公室也借不到Android12的设备。目测了下代码,感觉没啥问题,就提交了。
几个星期后,有客户反馈,Android 12上蓝牙,报蓝牙权限缺失导致的crash:
java.lang.SecurityException: Need BLUETOOTH permission
at android.bluetooth.BluetoothHeadset.<init>(BluetoothHeadset.java:426)
at android.bluetooth.BluetoothAdapter.getProfileProxy(BluetoothAdapter.java:3139)
看了下源码,crash在下面代码:
// Preserve legacy compatibility where apps were depending on
// registerStateChangeCallback() performing a permissions check which
// has been relaxed in modern platform versions
if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
&& context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Need BLUETOOTH permission");
}
也就是在Android12设备上运行的APP,如果targetSdkVersion小于31并且没有android.Manifest.permission.BLUETOOTH这个权限,运行到这里,就会抛出蓝牙权限缺失异常。
目前这个阶段,大部分客户的APP应该是没有适配Android12的,那么targetSdkVersion肯定是小于31,然后升级了之前我改动过的SDK,没有android.permission.BLUETOOTH
这个权限,导致crash。
这是第一个教训,没有经过验证的代码就有可能存在bug。
然后客户又说,老的SDK打包的APK在Android 12上可以运行,并且蓝牙相关功能也正常。
当时就觉得奇怪,既然你运行正常,那第一个客户是怎么crash的?
但是由于比较匆忙,也没细想,直接在相关代码里加了这么一个判断,规避了crash。
if (Build.VERSION.SDK_INT < 31 || context.getApplicationInfo().targetSdkVersion < 31) {
return checkPermission(context, BLUETOOTH); //对老权限校验,不通过则不往下执行。
}
于是又犯了第二个错误,没有理清bug的根源就胡乱修改。
后面越想越不对,新Feature不都应该是前向兼容的么?新的设备上运行targetSdkVersion还比较老的APP应该是没有问题才对。而且我的第二个修改会导致targetSdkVersion < 31的APP在android 12上无法运行,因为AndroidManifest
里的权限是新的:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
我的第二个修改肯定是不对的。而且Google肯定也不会犯这么低级的错误。
那么第一个客户又是怎么crash的呢?
于是又去询问第一个用户的,问他targetSdkVersion
是多少。
果然,他说是31。这下真相大白,targetSdkVersion指向了Android12,但是有没有做相应的代码适配,crash是必然。这个适配操作理应由客户端完成,而不是SDK。
于是,将SDK两次修改的代码全部回退,至此,问题告一段落。
好在我们的客户量不大, 不然上线的错误修改势必会导致大规模用户使用异常,这是灾难级别的。
以后去大厂了可不能犯这种低级错误,要理清问题根源在修改代码,牢记牢记。