该博文用来整理开源项目 Android-References 迁移到 AndroidX 过程中遇到的各种问题。
? Android-References 是一个 Android 示例程序项目:包含了 MVP, MVVM, 组件化, ARouter, RxJava, EventBus, ButterKnife, 视频播放, 视频直播, 网络访问, 布局和控件整理等,感性却可以到 Gihub:https://github.com/Shouheng88/Android-references 参考源码。
因为该项目包含了各种 support 包控件,并且各种组件化、架构模式和各种常见的功能一应俱全,因此,比较具有代表性。这里我们使用它来作为一个示例来将我们的项目迁移到 AndroidX。
1、关于 AndroidX
关于 AndroidX,可以参考: Hello world AndroidX
AndroidX 用来统一 Android 中的 support 包,之前我们通过引入 support 包的各个版本来使用支持包,现在我们可以通过使用 AndroidX 来使用支持包。从长远来看这当然是大有好处的,可以避免使用支持包中遇到的版本冲突、升级带来的各种问题。
不过,如果项目完全迁移到 AndroidX 风险还是太大。如果项目紧急的话,引入 AndroidX 成本都有些高,主要是因为一些三方库的原因。虽然一些使用特别多的三方库,比如 Glide 等都已经开始支持 AndroidX。当然,还有一些潜在的问题,比如使用字符串来获取类的 Behavior 等,迁移的时候可能就不会被照顾到。
为了迁移到 AndroidX,Google 给开发者提供了一个工具:在 AS 的 Refactor 中提供了一项 Migrate to AndroidX 的选项。但选择了迁移之后,出现一些问题还需要开发者自己手动解决。
2、着手迁移
2.1 第一个问题:Execution failed for task ‘:app:transformClassesWithMultidexlistForAlphaDebug’
这里我们迁移之后在 build 的时候出现了标题的问题:
* What went wrong:
Execution failed for task ':app:transformClassesWithMultidexlistForAlphaDebug'.
> com.android.build.api.transform.TransformException: Error while generating the main dex list.
显然是将 class 转换成 dex 的过程中出现了一些问题,不过只是上面的这行日志我们无法定位问题。所以,我们需要让 gradle 输出更多的错误信息,于是我们执行:
gradlew build --stacktrace
来让 Gradle 输出错误栈信息:
Caused by: com.android.builder.multidex.D8MainDexList$MainDexListException: com.android.tools.r8.errors.CompilationError: Program type already present: com.a
libaba.android.arouter.routes.ARouter$$Group$$library
at com.android.builder.multidex.D8MainDexList.generate(D8MainDexList.java:87)
at com.android.build.gradle.internal.transforms.D8MainDexListTransform.transform(D8MainDexListTransform.kt:131)
... 54 more
显然是 ARouter$$Group$$library
类的问题,我们使用 Ctrl+N
来搜索这个类,发现出现了两个同样的类。这个类是使用阿里的 ARouter 的时候在编译期间生成的:
按照上面的错误提示,该类同时出现在了我们的两个模块 libraries
和 layout
下面,因而类冲突了。所以,接下来的问题就是要发现为什么会出现类冲突。
经过一层层排查发现是一个地方写错了:
这个是 layout
模块下面的冲突的类,我们发现它的路由地址是 /library/swipe_back
,所以因为路由的地址是 library
的原因它在 layout
的模块下面生成了 ARouter$$Group$$Library
。按照正确的写法它应该是出现在 layout
模块下面,并且路由的地址是 /layout/swipe_back
,那样就应该被生成到 ARouter$$Group$$Layout
下面,就不会多出一个类 ARouter$$Group$$Library
了。
虽然,最终问题的原因很简单,但是我们看到,发现问题的过程中需要自己有思路的去排查,而不是除了问题立刻 Google 或者 SOF。
2.2 android.view.InflateException: Binary XML file line #14: Error inflating class
修改了上面的问题之后,我们的程序可以编译并且安装了。
然后,我们又遇到下面的问题:
android.view.InflateException: Binary XML file line #14: Error inflating class
这种问题比较常见,是 XML 的某个地方写错了,经过排查发现有一行代码,当我们为控件添加 Behavior 的时候使用了字符串形式的类名。在迁移的时候没有被照顾到:
事实上在 Google 的新的 material 包下面的 values.xml 文件中定义了一些 Behavior,新的包中这些字符串的值已经被替换过。但是像我们上面的这种情况,因为使用的是字符串而不是引用的资源,所以就没有被替换过去。因此,引用非自定义的 Behavior 的时候需要注意使用字符串资源进行引用而不是使用字符串:
2.3 android.view.InflateException: Binary XML file line #12: Binary XML file line #12: Error inflating class com.google.android.material.button.MaterialButton
这里的类 Support28Activity
用来整理原来的 support-28 包里面的一些控件,迁移之后页面打开的时候立即崩溃。然后留下了一地鸡毛(异常):
java.lang.RuntimeException: Unable to start activity ComponentInfo{me.shouheng.references/me.shouheng.layout.view.support28.Support28Activity}: android.view.InflateException: Binary XML file line #12: Binary XML file line #12: Error inflating class com.google.android.material.button.MaterialButton
我们尝试在程序中寻找 MaterialButton
,发现确实能够找到这个类,但这里加载失败是什么原因呢?
于是我们继续查看输出的错误日志,从这里我们获取到了更多的信息。
Process: me.shouheng.references, PID: 22961
java.lang.RuntimeException: Unable to start activity ComponentInfo{me.shouheng.references/me.shouheng.layout.view.support28.Support28Activity}: android.view.InflateException: Binary XML file line #12: Binary XML file line #12: Error inflating class com.google.android.material.button.MaterialButton
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3037)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3172)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1906)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.a