迁移到 AndroidX 过程中遇到的各种问题

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/github_35186068/article/details/83929124

该博文用来整理开源项目 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 的时候在编译期间生成的:

在这里插入图片描述

按照上面的错误提示,该类同时出现在了我们的两个模块 librarieslayout 下面,因而类冲突了。所以,接下来的问题就是要发现为什么会出现类冲突。

经过一层层排查发现是一个地方写错了:

在这里插入图片描述

这个是 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.app.ActivityThread.main(ActivityThread.java:6863)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: android.view.InflateException: Binary XML file line #12: Binary XML file line #12: Error inflating class com.google.android.material.button.MaterialButton
     Caused by: android.view.InflateException: Binary XML file line #12: Error inflating class com.google.android.material.button.MaterialButton
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Constructor.newInstance0(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
        at android.view.LayoutInflater.createView(LayoutInflater.java:647)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:790)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:863)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
        at androidx.databinding.DataBindingUtil.inflate(DataBindingUtil.java:126)
        at androidx.databinding.DataBindingUtil.inflate(DataBindingUtil.java:95)
        at me.shouheng.commons.view.activity.CommonActivity.onCreate(CommonActivity.java:41)
        at android.app.Activity.performCreate(Activity.java:7149)
        at android.app.Activity.performCreate(Activity.java:7140)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1288)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3017)
        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.app.ActivityThread.main(ActivityThread.java:6863)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.IllegalArgumentException: The style on this component requires your app theme to be Theme.MaterialComponents (or a descendant).
        at com.google.android.material.internal.ThemeEnforcement.checkTheme(ThemeEnforcement.java:240)
        at com.google.android.material.internal.ThemeEnforcement.checkMaterialTheme(ThemeEnforcement.java:215)
        at com.google.android.material.internal.ThemeEnforcement.checkCompatibleTheme(ThemeEnforcement.java:143)
2018-11-10 15:21:55.948 22961-22961/me.shouheng.references E/AndroidRuntime:     at com.google.android.material.internal.ThemeEnforcement.obtainStyledAttributes(ThemeEnforcement.java:78)
        at com.google.android.material.button.MaterialButton.<init>(MaterialButton.java:140)
        at com.google.android.material.button.MaterialButton.<init>(MaterialButton.java:133)
        	... 27 more

按照后面几行的意思:

     Caused by: java.lang.IllegalArgumentException: The style on this component requires your app theme to be Theme.MaterialComponents (or a descendant).

这个错误是因为我们设置的主题照成的。按照上面的意思,我们需要更新自己的控件的主题到 Theme.MaterialComponents。除了切换控件的主题,我们可以更新应用的主题,我们可以让自己的应用主题继承 Material Components Bridge 主题:

<style name="Theme.MyApp" parent="Theme.MaterialComponents.Light.Bridge">
    <!-- ... -->
</style>

可以根据自己的需要选择继承下面的几种主题:

Theme.MaterialComponents.Bridge
Theme.MaterialComponents.Light.Bridge
Theme.MaterialComponents.NoActionBar.Bridge
Theme.MaterialComponents.Light.NoActionBar.Bridge
Theme.MaterialComponents.Light.DarkActionBar.Bridge

或者在你之前的 AppCompact 主题之上增加一些新的属性:

<style name="Theme.MyApp" parent="Theme.AppCompat">

  <!-- Original AppCompat attributes. -->
  <item name="colorPrimary">@color/my_app_primary_color</item>
  <item name="colorSecondary">@color/my_app_secondary_color</item>
  <item name="android:colorBackground">@color/my_app_background_color</item>
  <item name="colorError">@color/my_app_error_color</item>

  <!-- New MaterialComponents attributes. -->
  <item name="colorPrimaryVariant">@color/my_app_primary_variant_color</item>
  <item name="colorSecondaryVariant">@color/my_app_secondary_variant_color</item>
  <item name="colorSurface">@color/my_app_surface_color</item>
  <item name="colorOnPrimary">@color/my_app_color_on_primary</item>
  <item name="colorOnSecondary">@color/my_app_color_on_secondary</item>
  <item name="colorOnBackground">@color/my_app_color_on_background</item>
  <item name="colorOnError">@color/my_app_color_on_error</item>
  <item name="colorOnSurface">@color/my_app_color_on_surface</item>
  <item name="scrimBackground">@color/mtrl_scrim_color</item>
  <item name="textAppearanceHeadline1">@style/TextAppearance.MaterialComponents.Headline1</item>
  <item name="textAppearanceHeadline2">@style/TextAppearance.MaterialComponents.Headline2</item>
  <item name="textAppearanceHeadline3">@style/TextAppearance.MaterialComponents.Headline3</item>
  <item name="textAppearanceHeadline4">@style/TextAppearance.MaterialComponents.Headline4</item>
  <item name="textAppearanceHeadline5">@style/TextAppearance.MaterialComponents.Headline5</item>
  <item name="textAppearanceHeadline6">@style/TextAppearance.MaterialComponents.Headline6</item>
  <item name="textAppearanceSubtitle1">@style/TextAppearance.MaterialComponents.Subtitle1</item>
  <item name="textAppearanceSubtitle2">@style/TextAppearance.MaterialComponents.Subtitle2</item>
  <item name="textAppearanceBody1">@style/TextAppearance.MaterialComponents.Body1</item>
  <item name="textAppearanceBody2">@style/TextAppearance.MaterialComponents.Body2</item>
  <item name="textAppearanceCaption">@style/TextAppearance.MaterialComponents.Caption</item>
  <item name="textAppearanceButton">@style/TextAppearance.MaterialComponents.Button</item>
  <item name="textAppearanceOverline">@style/TextAppearance.MaterialComponents.Overline</item>

</style>

显然,这里是要求你为了使自己的应用符合 Material 规范,需要预先定义一些属性值,然后会被应用到程序的控件中。

这里我们将主题稍做修改

    <!--<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">-->
    <style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar.Bridge">
    </style>

按照上面的方式,我们成功地打开了该页面。

2.4 java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed

然而打开了页面不久就又发现了问题,这个问题出现在 MaterialButton 的点击的时候:

    Process: me.shouheng.references, PID: 5730
    java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed
        at android.graphics.Canvas.checkValidClipOp(Canvas.java:779)
        at android.graphics.Canvas.clipRect(Canvas.java:826)
        at com.google.android.material.shape.MaterialShapeDrawable.prepareCanvasForShadow(MaterialShapeDrawable.java:850)
        at com.google.android.material.shape.MaterialShapeDrawable.draw(MaterialShapeDrawable.java:746)
        at android.view.View.getDrawableRenderNode(View.java:20622)
        at android.view.View.drawBackground(View.java:20558)
        at android.view.View.draw(View.java:20357)
        at android.view.View.updateDisplayListIfDirty(View.java:19241)
        at android.view.View.draw(View.java:20094)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4337)
        at androidx.coordinatorlayout.widget.CoordinatorLayout.drawChild(CoordinatorLayout.java:1246)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4116)
        at android.view.View.updateDisplayListIfDirty(View.java:19232)
        at android.view.View.draw(View.java:20094)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4337)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4116)
        at android.view.View.updateDisplayListIfDirty(View.java:19232)
        at android.view.View.draw(View.java:20094)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4337)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4116)
        at android.view.View.updateDisplayListIfDirty(View.java:19232)
        at android.view.View.draw(View.java:20094)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4337)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4116)
        at android.view.View.updateDisplayListIfDirty(View.java:19232)
        at android.view.View.draw(View.java:20094)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4337)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4116)
        at android.view.View.updateDisplayListIfDirty(View.java:19232)
        at android.view.View.draw(View.java:20094)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4337)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4116)
        at android.view.View.draw(View.java:20369)
        at com.android.internal.policy.DecorView.draw(DecorView.java:781)
        at android.view.View.updateDisplayListIfDirty(View.java:19241)
        at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:690)
        at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:696)
        at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:805)
        at android.view.ViewRootImpl.draw(ViewRootImpl.java:3515)
        at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:3312)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2681)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1633)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7786)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1004)
        at android.view.Choreographer.doCallbacks(Choreographer.java:816)
        at android.view.Choreographer.doFrame(Choreographer.java:751)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:990)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6863)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

经过排查发现,在 com.google.android.material.shape.MaterialShapeDrawable 中的 prepareCanvasForShadow() 方法中使用了 Region.Op.REPLACE

  private void prepareCanvasForShadow(Canvas canvas) {
    // ... 无关代码
    canvas.clipRect(canvasClipBounds, Region.Op.REPLACE);
    // ... 无关代码
  }

而在 support-28 中增加了下面的校验(这个校验在 support-27 上面是不存在的),显然是因为我们使用了 Region.Op.REPLACE 属性的原因:

    private static void checkValidClipOp(@NonNull Region.Op op) {
        if (sCompatiblityVersion >= Build.VERSION_CODES.P
                && op != Region.Op.INTERSECT && op != Region.Op.DIFFERENCE) {
            throw new IllegalArgumentException(
                    "Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed");
        }
    }

在该方法上面有真么几行注释:

Region.Op values other than Region.Op.INTERSECT and Region.Op.DIFFERENCE have the ability to expand the clip. The canvas clipping APIs are intended to only expand the clip as a result of a restore operation. This enables a view parent to clip a canvas to clearly define the maximal drawing area of its children. The recommended alternative calls are clipRect(Rect) and clipOutRect(Rect)

大体意思是:Region.Op.INTERSECT和Region.Op.DIFFERENCE以外的Region.Op值可以展开 Clip。画布剪辑API仅用于在还原操作后展开Clip。这使视图父级能够剪切画布以清楚地定义其子画面的最大绘制区域。推荐调用clipRect(Rect)和clipOutRect(Rect).

虽然我们找到了问题的原因,但是这个类究竟是在哪里用到的我们还无法定位到,因此,我们还需要进一步进行排查。

经过排查,我们发现出现问题的原因是 material 包中的 BottomAppBar 控件。其实从上面的栈中我们也可以看出这一点:明显是在绘制 View 树的子 View 的时候出现的异常,而且是绘制 CoordinatorLayout。在 BottomAppBar 中使用了 materialShapeDrawable,该变量是 MaterialShapeDrawable。正是出现问题的罪魁祸首。

这个类MaterialShapeDrawable是用来实现 BottomBar 的阴影效果的,虽然它是 BottomBar 的内部类,但是如果不希望它调用上面的那个方法也是可以的。

首先,我发现它要先判断是否具有阴影再调用绘制阴影的方法:

    if (hasCompatShadow()) {
      // Save the canvas before changing the clip bounds.
      canvas.save();

      prepareCanvasForShadow(canvas);
	  // ...无关代码
    }

而这里的判断是否具有阴影的方法如下:

  private boolean hasCompatShadow() {
    return shadowCompatMode != SHADOW_COMPAT_MODE_NEVER
        && shadowCompatRadius > 0
        && (shadowCompatMode == SHADOW_COMPAT_MODE_ALWAYS || requiresCompatShadow());
  }

我们可以通过为 BottomBar 设置移除阴影效果来避免这个类调用绘制阴影的方法。于是,

        <com.google.android.material.bottomappbar.BottomAppBar
            android:id="@+id/bottom_app_bar"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_gravity="bottom"
            app:backgroundTint="@color/colorAccent"
            app:fabAlignmentMode="center"
            app:elevation="0dp"
            app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior"/>

虽然效果不好看,但是阴影解决了。

2.5 android.view.InflateException: Binary XML file line #24: Binary XML file line #24: Error inflating class com.google.android.material.floatingactionbutton.FloatingActionButton

在排查上面问题的过程中,我们同样发现了 FAB 的一个问题。这个 Material 包中的 FAB 与 Support-28 包中的相比做了一些调整。且看下面的异常信息。这里问题显然是类无法记载造成的。那么具体是因为什么呢?我们往下看到错误栈的最后几行,发现是有一个属性没有找到。

2018-11-10 16:56:48.408 27581-27581/me.shouheng.references E/AndroidRuntime: FATAL EXCEPTION: main
    Process: me.shouheng.references, PID: 27581
    java.lang.RuntimeException: Unable to start activity ComponentInfo{me.shouheng.references/me.shouheng.layout.view.support28.BottomAppBarActivity}: android.view.InflateException: Binary XML file line #24: Binary XML file line #24: Error inflating class com.google.android.material.floatingactionbutton.FloatingActionButton
        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.app.ActivityThread.main(ActivityThread.java:6863)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: android.view.InflateException: Binary XML file line #24: Binary XML file line #24: Error inflating class com.google.android.material.floatingactionbutton.FloatingActionButton
     Caused by: android.view.InflateException: Binary XML file line #24: Error inflating class com.google.android.material.floatingactionbutton.FloatingActionButton
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Constructor.newInstance0(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
        at android.view.LayoutInflater.createView(LayoutInflater.java:647)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:790)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:863)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
        at androidx.databinding.DataBindingUtil.inflate(DataBindingUtil.java:126)
        at androidx.databinding.DataBindingUtil.inflate(DataBindingUtil.java:95)
        at me.shouheng.commons.view.activity.CommonActivity.onCreate(CommonActivity.java:41)
        at android.app.Activity.performCreate(Activity.java:7149)
        at android.app.Activity.performCreate(Activity.java:7140)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1288)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3017)
        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.app.ActivityThread.main(ActivityThread.java:6863)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.UnsupportedOperationException: Failed to resolve attribute at index 0: TypedValue{t=0x2/d=0x7f0300b3 a=3}
        at android.content.res.TypedArray.getColorStateList(TypedArray.java:565)
        at com.google.android.material.resources.MaterialResources.getColorStateList(MaterialResources.java:65)
        at com.google.android.material.floatingactionbutton.FloatingActionButton.<init>(FloatingActionButton.java:204)
2018-11-10 16:56:48.408 27581-27581/me.shouheng.references E/AndroidRuntime:     at com.google.android.material.floatingactionbutton.FloatingActionButton.<init>(FloatingActionButton.java:190)
        	... 27 more

根据错误的栈信息,我们到指定的方法下面去查看问题的原因,发现是设置背景着色的时候出现的问题:

    backgroundTint =
        MaterialResources.getColorStateList(
            context, a, R.styleable.FloatingActionButton_backgroundTint);

但我们并没有为 FAB 添加该属性,那么又是哪里出现的问题呢?原来是因为我们为 FAB 添加了一个 style:

style="@style/Widget.MaterialComponents.FloatingActionButton"

而该 style 的定义是:

  </style>
    <style name="Widget.MaterialComponents.FloatingActionButton" parent="Widget.Design.FloatingActionButton">
    <item name="enforceMaterialTheme">true</item>
    <item name="elevation">@dimen/mtrl_fab_elevation</item>
    <item name="backgroundTint">?attr/colorSecondary</item>
    <item name="tint">?attr/colorOnSecondary</item>
    <item name="hoveredFocusedTranslationZ">@dimen/mtrl_fab_translation_z_hovered_focused</item>
    <item name="pressedTranslationZ">@dimen/mtrl_fab_translation_z_pressed</item>
    <item name="rippleColor">@color/mtrl_fab_ripple_color</item>
    <item name="showMotionSpec">@animator/mtrl_fab_show_motion_spec</item>
    <item name="hideMotionSpec">@animator/mtrl_fab_hide_motion_spec</item>
  </style>

这样刚好与我们的问题吻合,所以解决的方案就是移除这个属性。

2.6 java.net.UnknownServiceException: CLEARTEXT communication to baobab.kaiyanapp.com not permitted by network security policy

按照上面的方式对我们的项目做了调整之后,我们发现项目已经可以运行了。接下来出现的问题是网络访问过程中出现的。当我们访问网络的时候会遇到下面的这个异常:

java.net.UnknownServiceException: CLEARTEXT communication to baobab.kaiyanapp.com not permitted by network security policy

这个是因为 Android P 中新引入了网络安全规则,以上内容会对使用 http 的 URL 出现,默认会禁止访问 http 类型的地址。

当然,通常我们发布的时候会使用 Https 类型的网络协议,而当开发和调试的时候可能就没有那么严格了。所以,为了解决这个问题, Android 新引入了下面的解决方案:

首先,创建配置文件 res/xml/network_security_config.xml,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">localhost</domain>
    </domain-config>
</network-security-config>

这里的 ``localhost是 host 的地址,比如我上面的出错的地址应该是kaiyanapp.com`。

然后,我们将其配置到 manifest.xml 中:

<application
        android:networkSecurityConfig="@xml/network_security_config"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity android:name=" (...)
    </application>

在我们的项目中使用了三个不同的地址,并且都是 http 的,所以就需要在该地址下面配置三个域名,然后再次访问网络,问题就解决了。

关于 Android 网络和安全的内容,建议参考官方文档:网络安全性配置 | Android Developers

总结

显然迁移到 AndroidX 的过程中还是会出现许多问题的,大多数是与 Android 的支持包有关的。按照我们上面遇到的问题,主要包括以下几点内容:

  1. 主题
  2. 支持库的控件(控件属性,实现方式好像变了一些)
  3. 三方库,许多库并没有迁移,所以会导致程序内标红,但是编译和运行没有问题
  4. 网络安全,网络安全部分做了一些调整

好了,这篇文章大致到这里。也希望通过这篇文章来让你了解下迁移到 AndroidX 过程中可能会出现什么问题,并以此来考虑迁移的成本。

1.Getting started with Material Components for Android
2.网络安全性配置 | Android Developers


如果您喜欢我的文章,可以在以下平台关注我:

展开阅读全文

没有更多推荐了,返回首页