最近遇到一个很诡异的问题,开始项目开发还比较顺利,但在即将发布的时候,却发现RN Android模块无法构建成功。报了以下的错:

error: resource android:attr/fontVariationSettings not found. Error:(14, 5) error: resource android:attr/ttcIndex not found.

报错原因分析

听同事反映他这边很早之前就在报这个错了,之前尝试了解决,未找到解决方法,今后都在我的机器上打包(我的机器竟然一直没有报错orz.,)。

网上搜了一下这个报错,大多数建议的解决方案是:

try to change the compileSdkVersion to: compileSdkVersion 28

意思是更新android的编译版本,证明这个报错是由于使用sdk 28的api所致的。

更新为这个构建版本后,发现又报了其他的错,而且项目即将发布,直接升级android compileVersion风险太大。

看了一下我们的android构建版本,指定的为27,难道是其他的子项目使用更高的构建版本导致的?

为了验证这个原因,我强制设置所有项目android的support-v4包构建版本至27.0.1,如下:

configurations.all {
resolutionStrategy.force 'com.android.support:support-v4:27.1.0'
}

发现并没有用,还是报的以前的错。这个时候就有点懵b了,已经强制设置了项目的support-v4包版本。为什么还会报错呢?

在stackoverflow上继续寻找了一些问题的答案,看到这样的一个答案:

For those that must keep compileSdkVersion 27 and are unable to upgrade to androidx yet, you must not upgrade to (or over) the versions of dependencies in the following links. These links are where the breaking change was introduced. You must find an earlier version that doesn't use androidx.

看来是使用androidx这个东西的包,使用了sdk 28版本导致了这个问题。所以我们需要找到哪些依赖使用androidx,需要对这些包进行处理。

在项目根路径下,执行gradlew -q app:dependencies(-q表示-quite,只打印error),查看项目的依赖情况,查看项目中有两个RN插件包含了androidx的依赖,react-native-camera的依赖太多,未贴完整。

+--- project :react-native-device-info
| +--- com.facebook.react:react-native:+ -> 0.57.7 (*)
| \--- com.google.android.gms:play-services-gcm:+ -> 17.0.0
| +--- androidx.collection:collection:1.0.0 (*)
| +--- androidx.core:core:1.0.0 (*)
| +--- androidx.legacy:legacy-support-core-utils:1.0.0 (*)
| +--- com.google.android.gms:play-services-base:17.0.0 (*)
| +--- com.google.android.gms:play-services-basement:17.0.0 (*)
| +--- com.google.android.gms:play-services-iid:[17.0.0] -> 17.0.0
| | +--- androidx.collection:collection:1.0.0 (*)
| | +--- androidx.core:core:1.0.0 (*)
| | +--- com.google.android.gms:play-services-base:17.0.0 (*)
| | +--- com.google.android.gms:play-services-basement:17.0.0 (*)
| | +--- com.google.android.gms:play-services-stats:17.0.0
| | | +--- androidx.legacy:legacy-support-core-utils:1.0.0 (*)
| | | \--- com.google.android.gms:play-services-basement:17.0.0 (*)
| | \--- com.google.android.gms:play-services-tasks:17.0.0 (*)
| \--- com.google.android.gms:play-services-stats:17.0.0 (*)
+--- project :react-native-camera
| +--- com.google.android.gms:play-services-vision:15.0.2
| | +--- com.google.android.gms:play-services-base:[15.0.1,16.0.0) -> 17.0.0
| | | +--- androidx.collection:collection:1.0.0
| | | | \--- androidx.annotation:annotation:1.0.0
| | | +--- androidx.core:core:1.0.0
| | | | +--- androidx.annotation:annotation:1.0.0
| | | | +--- androidx.collection:collection:1.0.0 (*)
| | | | +--- androidx.lifecycle:lifecycle-runtime:2.0.0
| | | | | +--- androidx.lifecycle:lifecycle-common:2.0.0
| | | | | | \--- androidx.annotation:annotation:1.0.0
| | | | | +--- androidx.arch.core:core-common:2.0.0
| | | | | | \--- androidx.annotation:annotation:1.0.0
| | | | | \--- androidx.annotation:annotation:1.0.0
| | | | \--- androidx.versionedparcelable:versionedparcelable:1.0.0
| | | | +--- androidx.annotation:annotation:1.0.0
| | | | \--- androidx.collection:collection:1.0.0 (*)

查看了两个插件的更新记录,在device info这个插件找到了最近的androidx的相关猫腻,新版本对androidx进行了support,所以我大致定位到很大可能是由于这个插件未更新导致的(目前项目使用package-lock.json,对插件版本进行了锁定)。

为了验证我的想法,我对这两个插件做了排除法处理,发现在移除react-native-device-info这个插件后,build不会再报那个错了(会报其他错,后面会讲这个的处理)。而移除react-native-camera后,项目依然会报那个错。

通过这个验证了我的想法,是由于react-native-device-info引入的androidx依赖导致使用了以上报错的api,导致了项目构建错误。

解决问题

找到报错原因后,怎么解决问题呢?

我们看到react-native-device-info最近进行了更新,对androidx进行了支持,我们先更新react-native-device-info至最新稳定版本(目前我这边使用的2.1.4),执行:

npm i react-native-device-info@latest --save

更新之后,重新build,发现报错:

RNDeviceModule.java:394: 错误: 找不到符号

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {

符号: 变量 P

位置: 类 VERSION_CODES

/RNDeviceModule.java:396: 错误: 找不到符号

locationEnabled = mLocationManager.isLocationEnabled();

^

符号: 方法 isLocationEnabled()

位置: 类型为LocationManager的变量 mLocationManager

这是由于使用了API 28导致的,找到报错的代码,发现该function是export给RN端使用的。目前我们并没有使用该function,可以暂时将这段代码注释。

注释之后,重新编译,即可编译成功。

PS:如果可以承担一些风险,可以整体升级项目的sdk build version至28,这样就可以不用注释这段代码。

总结

发现跟踪这类的问题,首要目标是还原问题,并找到问题产生的根源。只要根源可以确定,就可以评估所有的解决方法。使用哪种解决方法,取决项目的状态。