一次集成与升级AndroidX的记录

Posted 疯狂小芋头

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一次集成与升级AndroidX的记录相关的知识,希望对你有一定的参考价值。

文章目录


最近将某个项目升级到了 androidX,中间出现了很多问题,特别记录一下。

由于整个过程很复杂,需要简单说明一下。大概的过程如下:

1. 未升级 AndroidX 之前

  1. 首先是原来没有打算升级 AndroidX 的,只是想集成阿里的推送;但是发现一直无法下载到库,gradle 一直报错。

1.1. Connection refused

连接被拒绝,试了非常多次,不科学上网/部分科学上网/全局科学上网,都是一样的(此时 gradle 版本为 3.0.2)。

Connection refused

偶然间将 gradle 版本升级到最新版本 3.3.2,不再出现该连接拒绝的错误;我不禁陷入了深深地沉思……
实际上之前已经尝试独立集成过一次阿里推送并且正常的(在一个DEMO中只做了阿里推送的集成),所以再回去把项目翻出来查看,那个项目是真的也用了3.3.2版本的 gradle,暂时我将它定义为确实需要新版本的 gradle 才能正确连接到阿里的仓库,至少我感觉在旧版本上成功率可能会低很多。

  1. 升级后同步仓库后发现配置文件无法合并

1.2. Could not execute build using Gradle distribution

这是 gradle 同步时出现的错误信息,如果抛出的异常原因不清楚,直接往下看Caused by:...,即原始异常信息,查找下去可以看到出错的原因。

Error:Internal error:
org.gradle.tooling.BuildException: Could not execute build using Gradle distribution 'https://services.gradle.org/distributions/gradle-4.10.1-all.zip'.
	at org.gradle.tooling.internal.consumer.ExceptionTransformer.transform(ExceptionTransformer.java:51)
    ...
Caused by: org.gradle.internal.exceptions.LocationAwareException: Execution failed for task ':base:processDebugAndroidTestManifest'.
	at org.gradle.initialization.DefaultExceptionAnalyser.transform(DefaultExceptionAnalyser.java:74)
    ...
Caused by: org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':base:processDebugAndroidTestManifest'.
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:110)
	... 67 more
Caused by: java.lang.RuntimeException: Manifest merger failed with multiple errors, see logs
	... 112 more

排查操作:
在最后我们可以看到是由于Manifest merger failed with multiple errors合并 AndroidMainfest.xml 文件出错导致的,所以问题肯定就在这里了。详细的错误信息查看 logs ,即在下面面板中 gradle build 的控制输出面板gradle console。如果是新版本的 AS(3.3.2)则在 build 时出现错误会在右边有很明显的错误信息,不需要手动去点开控制台查看错误信息。

1.3. > Task :base:processDebugAndroidTestManifest FAILED

是这合并 AndroidMainfest.xml 时报错,具体的出错信息需要看下面

	> Task :base:processDebugAndroidTestManifest FAILED
[com.pgyersdk:sdk:3.0.0] /Users/xxx/.gradle/caches/transforms-1/files-1.1/sdk-3.0.0.aar/af4ea8a5066b778464e4804d6cf07040/AndroidManifest.xml:9:5-79 Warning:
	Element uses-permission#android.permission.ACCESS_NETWORK_STATE at [com.pgyersdk:sdk:3.0.0] AndroidManifest.xml:9:5-79 duplicated with element declared at [com.pgyersdk:sdk:3.0.0] AndroidManifest.xml:8:5-79
[com.aliyun.ams:alicloud-android-push:3.1.4] /Users/xxx/.gradle/caches/transforms-1/files-1.1/alicloud-android-push-3.1.4.aar/66b91b217d8fbce6cb4c4474bbb7d805/AndroidManifest.xml:26:5-81 Warning:
	Element uses-permission#android.permission.WRITE_EXTERNAL_STORAGE at [com.aliyun.ams:alicloud-android-push:3.1.4] AndroidManifest.xml:26:5-81 duplicated with element declared at [com.aliyun.ams:alicloud-android-push:3.1.4] AndroidManifest.xml:16:5-81

排查操作:
从错误信息中可以明确知道,就是因为 AndroidMainfest.xml 里的权限重复了导致了错误Element uses-permission#xxx at 某个库的 AndroidManifest.xml:9:5-79 duplicated with element declared at 某个库的 AndroidManifest.xml:8:5-79,并且错误位置的行号都明确给出来了,所以按着行号去查,将重复的权限注释掉就行了。大部分时候可能是你自己应用里的 AndroidMainfest.xml 的权限已经在依赖库里申请了,所以就重复了。

重量级的坑爹问题出现了!!仔细看上面的重复错误

Element uses-permission# xxx at [com.pgyersdk:sdk:3.0.0] xxx duplicated with element declared at [com.pgyersdk:sdk:3.0.0]

是的,你没有看错,[com.pgyersdk:sdk:3.0.0]依赖库自己的 AndroidMainfest.xml 里的权限重复了!!!,这个问题是个大坑,因为一开始没有细看以为自己的 AndroidMainfest.xml 的权限与库重复了,即使注释掉了自己项目里的权限依然一直报错!根据错误信息,我们知道了这个库的位置:/Users/xxx/.gradle/caches/transforms-1/files-1.1/sdk-3.0.0.aar/af4ea8a5066b778464e4804d6cf07040/AndroidManifest.xml

打开该位置的配置文件,确实文件中是出现重复的权限!

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!--这里的权限是真的重复了-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

删除掉重复的权限,再重新 build 错误不再出现。PS:细心的小伙伴会发现,上面编译里报错的,还有阿里的包【疲惫的微笑.jpg】。

注意:这件事提醒我们,新的 gradle 版本对检查更加严格;并且在自己也需要提供给别人库或者 module 时,没必要的权限不要申请,重复的权限删除掉,不然可能你永远不知道下一个坑的人是谁。

另外,在解决这个问题的时候查到一些移除库中权限的方法,虽然这里并没有使用到,但是作者很用心测试并说明了情况,这个备注一下。
一次Android权限删除经历

1.4. Attribute application@appComponentFactory

这个错误很奇怪,也很不清晰。首先根据错误信息,在相应的路径下的 xml 文件存在问题/Users/xxx/android/sdk_demo/base/build/intermediates/tmp/manifest/androidTest/debug/manifestMerger5738855137681502227.xml,但是实际上去对应的路径下查找时,是真的查找到不到这个文件!!!

/Users/xxx/android/sdk_demo/base/build/intermediates/tmp/manifest/androidTest/debug/manifestMerger5738855137681502227.xml:22:18-91 Error:
	Attribute application@appComponentFactory value=(android.support.v4.app.CoreComponentFactory) from [com.android.support:support-compat:28.0.0] AndroidManifest.xml:22:18-91
	is also present at [androidx.core:core:1.0.0] AndroidManifest.xml:22:18-86 value=(androidx.core.app.CoreComponentFactory).
	Suggestion: add 'tools:replace="android:appComponentFactory"' to <application> element at manifestMerger5738855137681502227.xml:7:5-9:19 to override.

后面 gradle 也给出了建议的解决方案:Suggestion: add 'tools:replace="android:appComponentFactory"' to <application> element at manifestMerger5738855137681502227.xml:7:5-9:19 to override.

使用该方案在自己的项目里添加了该参数后,无效。根据网上的小伙伴提示,实际上除了添加这个tools:replace="android:appComponentFactory",还需要再添加一个任意值的属性android:appComponentFactory="任意值",我理解是后面添加的“任意值”是为了覆盖所有冲突地方的数据。
详细请参考解决 导入三方时出现: appComponentFactory 错误

虽然添加了这个操作,但是在当时依然是无法正常编译(但是不是还是这个问题我不太记得了),并且后面在将项目升级到 AndroidX 之后,实际上去掉这部分操作也是可以正常编译并运行的,怀疑可能是不需要的。

2. 升级到 AndroidX

  1. 由于 gradle 版本升级到3.3.2,项目中使用的 butterknife 要求需要升级到 10.1.0(支持 AndroidX),开始了痛苦的升级过程

2.1. 升级转换到 AndroidX

升级 AS 到最新版本3.3.2,此版本提供了Refactor->Migrate to AndroidX功能,可以快速转换到 AndroidX,但是并不是全部,如部分 support 包是无法转换的

2.2. support 升级到 AndroidX

如果原项目使用了 support 包的 annotation,如@Nullable@NonNull,请毫不犹豫在依赖库中添加上以下依赖

api 'androidx.annotation:annotation:1.0.2'

并全局文件搜索将原引用全部替换为新的引用(这种替换方式是最快的,会有误操作的部分,但是影响不大)。

原引用新引用
android.support.annotation.androidx.annotation.

2.3. 控件升级到 AndroidX

  • TabLayout

如果原项目使用了 design 包如TabLayout,请毫不犹豫在依赖库中添加上以下依赖,design 包中原本有的TabLayout其实是属于 support 的包名下,但是在 AndroidX 中不会默认包含到androidx.core.xxx的包下

api 'com.android.support:design:28.0.0'
原引用新引用
android.support.design.widget.TabLayoutcom.google.android.material.tabs.TabLayout
  • Constraintlayout

如果原项目使用了Constraintlayout,请毫不犹豫在依赖库中添加上以下依赖

api 'androidx.constraintlayout:constraintlayout:1.1.3'
原引用新引用
android.support.constraint.ConstraintLayoutandroidx.constraintlayout.widget.ConstraintLayout
  • RecyclerView、SwipeRefreshLayout、Guideline、NestedScrollView
    这些控件都需要更新
原引用新引用
android.support.v7.widget.RecyclerViewandroidx.recyclerview.widget.RecyclerView
android.support.v4.widget.SwipeRefreshLayoutandroidx.swiperefreshlayout.widget.SwipeRefreshLayout
android.support.constraint.Guidelineandroidx.constraintlayout.widget.Guideline
android.support.v4.widget.NestedScrollViewandroidx.core.widget.NestedScrollView

注意:所有的控件在更新引用时,需要把 xml 文件中的控件也一起更新

2.4. Program type already present

引用库文件重复

AGPBI: "kind":"error","text":"Program type already present: android.support.v4.graphics.drawable.IconCompatParcelizer","sources":[],"tool":"D8"
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:transformDexArchiveWithExternalLibsDexMergerForGatewayDebug'.
> com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives: 
  Program type already present: android.support.v4.graphics.drawable.IconCompatParcelizer
  Learn how to resolve the issue at https://developer.android.com/studio/build/dependencies#duplicate_classes.

根据错误信息,可以知道是android.support.v4.graphics.drawable.IconCompatParcelizer这个类重复了,但是我们并不一定知道是在什么库导致的重复,所以可以全局搜索。这里使用全局搜索文件是可能查找不到的,需要使用双 shift,即 AS 自带的searchEverywhere

一般情况下是因为 AndroidX 默认提供了原有的大部分 support 包,这里很可能是重复引入了原有的com.android.support包导致的,建议取消掉所有的com.android.support的依赖

2.5. Static interface methods are only supported starting with Android N

引用库中使用了静态接口方法,该功能只在 java8 中支持,所以需要指定编译的 java 版本为 java8

AGPBI: "kind":"error","text":"Static interface methods are only supported starting with Android N (--min-api 24): void butterknife.Unbinder.lambda$static$0()","sources":[],"tool":"D8"

在主项目(一定要在主项目,一般是 app module)中添加上以下的编译选项:

android
    ...
    compileOptions 
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    
    ...

这个是由于 butterknife 10.0.1 版本中使用了静态接口的原因引起的,如果你在其它子 module 中使用了 butterknife,注意上面的编译选项也是需要在主 module 中添加,在子 module 中添加是无效的,这个在实际中已经测试过了(即不能只在引用 butterknife 的 module 中添加,需要在主 module 中添加该配置)
相关参考内容As升级到3.3出现的报错

2.6. Error:Unable to resolve dependency for ‘:@debug/compileClasspath’

在升级后由于阿里的仓库很容易查找不到或者同步失败,切换过 gradle 的 offwork 离线同步操作。后面非常频繁出现以下问题:

Error:Unable to resolve dependency for ':@debug/compileClasspath'
//或者类似
Could not resolve com.aliyun.ams:alicloud-android-utils:latest.integration. Show Details

各种尝试不科学上网或科学上网都无法解决问题,或者是很偶然能同步成功一两次。该情况下需要确认gradle.properties文件中是否设置了代理,如果设置了代理很有可能就是它引引的,在注释了该文件中的代理后,同步不再频繁出现失败的情况
参考文章:
Android Studio 3.1.4,gradle 4.4解决Error:Unable to resolve dependency for ':@debug/compileClasspath’问题
关于升级到Android studion 3.1.3 gradle 4.4 遇到的坑

2.7. ACCS_CHANNEL_INIT_FAIL 10212 静默连接进程(默认为channel进程)未初始化

使用阿里推送初始化时,可能会报出以下错误。在官方文档中查询到的解决方案并不完全适用。出现这个问题时需要注意:初始化时必须在Application的onCreate()中初始化

2.8. 其它

  1. 在升级中有出现过类似error: resource android:attr/fontVariationSettings resource android:attr/ttcIndex not found的错误,根据网上信息是 support 包的版本不正确,可以通过以下方式处理资源属性未查找到,但是实际上如果是已经升级到 AndroidX,把所有 support 包依赖去掉,只使用 AndroidX 中统一的包即可,不会有这种问题

3. 小结

总的来说升级 AndroidX 的过程并不是很愉快,可能各种问题都有吧,gradle 又时不时老是要同步,不管改的东西是不是依赖还只是改一个版本号。
另外阿里的仓库是真的很折腾人,包括文档也是很难找,基本是对阿里没有太多的好感了。
最后,感谢所有在网上发布自己经验的人,希望这个文章也能帮到别人。

部分参考文章 :

  1. Android:你好,androidX!再见,android.support

以上是关于一次集成与升级AndroidX的记录的主要内容,如果未能解决你的问题,请参考以下文章

AndroidX适配与升级指南

AndroidX适配与升级指南

AndroidX适配与升级指南

错误记录Android Studio 集成 ARoute 编译报错 ( 兼容 support 库和 androidx 库 | add ‘tools:replace=“android:appCo )

AGP 3.5.3升级到4.2.2问题记录

融云 IMKit SDK 5.X 升级说明