使用 fontFamily 在 TextView 中启用 R8 会导致 "android.view.InflateException Error inflating class TextView"

Enabling R8 causes "android.view.InflateException Error inflating class TextView" in TextView with fontFamily

我正在尝试 shrink/obfuscate 使用 R8 (AS 3.3) 构建我的发布版本,但它在尝试扩充 XML 文件时崩溃,特别是在带有 fontFamily 的 TextView 上。删除 fontFamily 属性可解决此问题。

XML 文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/blue_linear_gradient"
    tools:context=".login.LoginFragment">

    <ImageView
        android:id="@+id/familyIcon"
        android:layout_width="90dp"
        android:layout_height="90dp"
        android:layout_marginTop="32dp"
        app:srcCompat="@drawable/auth_login_icon"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView   <!-- This is line 20 -->
        android:id="@+id/title"
        android:layout_width="180dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:lineHeight="24dp"
        android:maxWidth="168dp"
        android:text="@string/login_title"
        android:textAlignment="center"
        android:fontFamily="@font/gotham_bold" <!-- Removing this line fixes it -->
        android:textColor="@color/kineduWhite"
        android:textSize="20sp"
        app:layout_constraintBottom_toTopOf="@+id/authForm"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/familyIcon" />

    <include
        android:id="@+id/authForm"
        layout="@layout/auth_form"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="8dp"
        android:maxWidth="324dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/familyIcon" />

    <!-- More views... -->

</androidx.constraintlayout.widget.ConstraintLayout>

这是堆栈跟踪:

2019-01-17 17:37:26.496 24514-24514/? E/AndroidRuntime: FATAL EXCEPTION: main
    android.view.InflateException: Binary XML file line #20: Binary XML file line #20: Error inflating class TextView
    Caused by: android.view.InflateException: Binary XML file line #20: Error inflating class TextView
    Caused by: java.lang.NullPointerException: Attempt to read from field 'long android.content.res.XmlBlock$Parser.mParseState' on a null object reference
        at android.content.res.XmlBlock$Parser.getAttributeListValue(XmlBlock.java:320)
        at androidx.core.content.res.FontResourcesParserCompat.androidx.core.content.res.FontResourcesParserCompat$FamilyResourceEntry parse(org.xmlpull.v1.XmlPullParser,android.content.res.Resources)(SourceFile:1)
        at androidx.core.content.res.ResourcesCompat.android.graphics.Typeface loadFont(android.content.Context,android.content.res.Resources,android.util.TypedValue,int,int,androidx.core.content.res.ResourcesCompat$FontCallback,android.os.Handler,boolean)(SourceFile:14)
        at androidx.core.content.res.ResourcesCompat.android.graphics.Typeface loadFont(android.content.Context,int,android.util.TypedValue,int,androidx.core.content.res.ResourcesCompat$FontCallback,android.os.Handler,boolean)(SourceFile:3)
        at androidx.core.content.res.ResourcesCompat.android.graphics.Typeface getFont(android.content.Context,int,android.util.TypedValue,int,androidx.core.content.res.ResourcesCompat$FontCallback)(SourceFile:8)
        at androidx.appcompat.widget.TintTypedArray.android.graphics.Typeface getFont(int,int,androidx.core.content.res.ResourcesCompat$FontCallback)(SourceFile:8)
        at androidx.appcompat.widget.AppCompatTextHelper.void updateTypefaceAndStyle(android.content.Context,androidx.appcompat.widget.TintTypedArray)(SourceFile:102)
        at androidx.appcompat.widget.AppCompatTextHelper.void loadFromAttributes(android.util.AttributeSet,int)(SourceFile:59)
        at androidx.appcompat.widget.AppCompatTextView.void <init>(android.content.Context,android.util.AttributeSet,int)(SourceFile:7)
        at androidx.appcompat.widget.AppCompatTextView.void <init>(android.content.Context,android.util.AttributeSet)(SourceFile:2)
        at androidx.appcompat.app.AppCompatViewInflater.androidx.appcompat.widget.AppCompatTextView createTextView(android.content.Context,android.util.AttributeSet)(SourceFile:1)
        at androidx.appcompat.app.AppCompatViewInflater.android.view.View createView(android.view.View,java.lang.String,android.content.Context,android.util.AttributeSet,boolean,boolean,boolean,boolean)(SourceFile:30)
        at androidx.appcompat.app.AppCompatDelegateImpl.void onSubDecorInstalled(android.view.ViewGroup)(SourceFile:89)
                                                        android.view.View createView(android.view.View,java.lang.String,android.content.Context,android.util.AttributeSet)
        at androidx.appcompat.app.AppCompatDelegateImpl.android.view.View onCreateView(android.view.View,java.lang.String,android.content.Context,android.util.AttributeSet)(SourceFile:1)
        at android.view.LayoutInflater$FactoryMerger.onCreateView(LayoutInflater.java:189)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:772)
        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 com.company.auth.login.LoginFragment.android.view.View onCreateView(android.view.LayoutInflater,android.view.ViewGroup,android.os.Bundle)(SourceFile:7)

我的字体位于单独的库模块中。

:core

中的

字体家族 xml 文件 "res/font/gotham-bold.xml"

<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
    <font
        app:fontStyle="normal"
        app:fontWeight="400"
        app:font="@font/gotham_rnd_bold"/>

    <font
        app:fontStyle="italic"
        app:fontWeight="400"
        app:font="@font/gotham_rnd_bold_italic"/>
</font-family>

而实际字体 "gotham_rnd_bold.otf" 也在这个 :core 模块中 "res/font/

我 post 在这里 R8 开发人员(来自您的 issue)提供更好的索引和参考的原因。

This happens because you are including program classes in your app that is already in the Android framework. The Proguard configuration rule -dontwarn org.xmlpull.v1.** explicitly disables these warnings from the compiler.

Ideally, you should not include any classes in your app that is also in the Android framework. You should be able to identify the dependency that includes org.xmlpull.* and then exclude that module from the dependency. It seems that others have successfully done this, see for example . If you manage to do this, then you shouldn't get any warnings even if you remove the rule -dontwarn org.xmlpull.v1.**.

Alternatively, you can use the rule -keep class org.xmlpull.** { *;}. This trick appears to be used by others (https://github.com/search?q=org.xmlpull.v1+extension=pro&type=Code), but it can be error-prone since your version of org.xmlpull.* may be different from the one in the Android framework.

Note that we are working on changing the behavior of R8 such that it deals with duplicate classes in a way such that Android framework classes always take precedence of program classes (b/120884788). In principle, none of the above changes should be needed with that fix.

您可以通过编程方式设置自定义字体,例如;

customText.typeface = ResourcesCompat.getFont(this, 
R.font.your_font)