該博文用來整理開源項目 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,能夠參考: Hello world AndroidXgit
AndroidX 用來統一 Android 中的 support 包,以前咱們經過引入 support 包的各個版原本使用支持包,如今咱們能夠經過使用 AndroidX 來使用支持包。從長遠來看這固然是大有好處的,能夠避免使用支持包中遇到的版本衝突、升級帶來的各類問題。github
不過,若是項目徹底遷移到 AndroidX 風險仍是太大。若是項目緊急的話,引入 AndroidX 成本都有些高,主要是由於一些三方庫的緣由。雖然一些使用特別多的三方庫,好比 Glide 等都已經開始支持 AndroidX。固然,還有一些潛在的問題,好比使用字符串來獲取類的 Behavior 等,遷移的時候可能就不會被照顧到。web
爲了遷移到 AndroidX,Google 給開發者提供了一個工具:在 AS 的 Refactor 中提供了一項 Migrate to AndroidX 的選項。但選擇了遷移以後,出現一些問題還須要開發者本身手動解決。canvas
這裏咱們遷移以後在 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 的時候在編譯期間生成的:
按照上面的錯誤提示,該類同時出如今了咱們的兩個模塊 libraries
和 layout
下面,於是類衝突了。因此,接下來的問題就是要發現爲何會出現類衝突。
通過一層層排查發現是一個地方寫錯了:
這個是 layout
模塊下面的衝突的類,咱們發現它的路由地址是 /library/swipe_back
,因此由於路由的地址是 library
的緣由它在 layout
的模塊下面生成了 ARouter$$Group$$Library
。按照正確的寫法它應該是出如今 layout
模塊下面,而且路由的地址是 /layout/swipe_back
,那樣就應該被生成到 ARouter$$Group$$Layout
下面,就不會多出一個類 ARouter$$Group$$Library
了。
雖然,最終問題的緣由很簡單,可是咱們看到,發現問題的過程當中須要本身有思路的去排查,而不是除了問題馬上 Google 或者 SOF。
修改了上面的問題以後,咱們的程序能夠編譯而且安裝了。
而後,咱們又遇到下面的問題:
android.view.InflateException: Binary XML file line #14: Error inflating class
這種問題比較常見,是 XML 的某個地方寫錯了,通過排查發現有一行代碼,當咱們爲控件添加 Behavior 的時候使用了字符串形式的類名。在遷移的時候沒有被照顧到:
事實上在 Google 的新的 material 包下面的 values.xml 文件中定義了一些 Behavior,新的包中這些字符串的值已經被替換過。可是像咱們上面的這種狀況,由於使用的是字符串而不是引用的資源,因此就沒有被替換過去。所以,引用非自定義的 Behavior 的時候須要注意使用字符串資源進行引用而不是使用字符串:
這裏的類 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>
按照上面的方式,咱們成功地打開了該頁面。
然而打開了頁面不久就又發現了問題,這個問題出如今 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"/>
雖然效果很差看,可是陰影解決了。
在排查上面問題的過程當中,咱們一樣發現了 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>
這樣恰好與咱們的問題吻合,因此解決的方案就是移除這個屬性。
按照上面的方式對咱們的項目作了調整以後,咱們發現項目已經能夠運行了。接下來出現的問題是網絡訪問過程當中出現的。當咱們訪問網絡的時候會遇到下面的這個異常:
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 的支持包有關的。按照咱們上面遇到的問題,主要包括如下幾點內容:
好了,這篇文章大體到這裏。也但願經過這篇文章來讓你瞭解下遷移到 AndroidX 過程當中可能會出現什麼問題,並以此來考慮遷移的成本。
1.Getting started with Material Components for Android
2.網絡安全性配置 | Android Developers
若是您喜歡個人文章,能夠在如下平臺關注我: