遷移到 AndroidX 過程當中遇到的各類問題

該博文用來整理開源項目 Android-References 遷移到 AndroidX 過程當中遇到的各類問題。html

? Android-References 是一個 Android 示例程序項目:包含了 MVP, MVVM, 組件化, ARouter, RxJava, EventBus, ButterKnife, 視頻播放, 視頻直播, 網絡訪問, 佈局和控件整理等,感性卻能夠到 Gihub:https://github.com/Shouheng88/Android-references 參考源碼。java

由於該項目包含了各類 support 包控件,而且各類組件化、架構模式和各類常見的功能包羅萬象,所以,比較具備表明性。這裏咱們使用它來做爲一個示例來將咱們的項目遷移到 AndroidX。android

一、關於 AndroidX

關於 AndroidX,能夠參考: Hello world AndroidXgit

AndroidX 用來統一 Android 中的 support 包,以前咱們經過引入 support 包的各個版原本使用支持包,如今咱們能夠經過使用 AndroidX 來使用支持包。從長遠來看這固然是大有好處的,能夠避免使用支持包中遇到的版本衝突、升級帶來的各類問題。github

不過,若是項目徹底遷移到 AndroidX 風險仍是太大。若是項目緊急的話,引入 AndroidX 成本都有些高,主要是由於一些三方庫的緣由。雖然一些使用特別多的三方庫,好比 Glide 等都已經開始支持 AndroidX。固然,還有一些潛在的問題,好比使用字符串來獲取類的 Behavior 等,遷移的時候可能就不會被照顧到。web

爲了遷移到 AndroidX,Google 給開發者提供了一個工具:在 AS 的 Refactor 中提供了一項 Migrate to AndroidX 的選項。但選擇了遷移以後,出現一些問題還須要開發者本身手動解決。canvas

二、着手遷移

2.1 第一個問題:Execution failed for task ‘:app:transformClassesWithMultidexlistForAlphaDebug’

這裏咱們遷移以後在 build 的時候出現了標題的問題:api

* 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


若是您喜歡個人文章,能夠在如下平臺關注我: