【问题标题】:Proguard causing runtime exception with Android Navigation ComponentProguard 导致 Android 导航组件运行时异常
【发布时间】:2018-10-26 23:25:39
【问题描述】:

在将 NavigationComponent (android.arch.navigation:navigation-fragment-ktx:1.0.0-alpha01) 集成到我的目标项目中并编译 sdk 为 27 后,我在使用 proguard 时遇到了这个崩溃

    2018-05-16 12:13:14.044 24573-24573/com.mypackage.myapp.x E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.mypackage.myapp.x, PID: 24573
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mypackage.myapp.x/com.mypackage.myapp.MainActivity}: android.view.InflateException: Binary XML file line #16: Binary XML file line #16: Error inflating class fragment
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2925)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3060)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:110)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1800)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6649)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:826)
     Caused by: android.view.InflateException: Binary XML file line #16: Binary XML file line #16: Error inflating class fragment
     Caused by: android.view.InflateException: Binary XML file line #16: Error inflating class fragment
     Caused by: java.lang.RuntimeException: Exception inflating com.mypackage.myapp.x:navigation/nav_graph line 7
        at androidx.navigation.j.a(Unknown Source:124)
        at androidx.navigation.d.a(Unknown Source:4)
        at androidx.navigation.fragment.NavHostFragment.a(Unknown Source:88)
        at android.support.v4.app.Fragment.l(Unknown Source:15)
        at android.support.v4.app.m.a(Unknown Source:369)
        at android.support.v4.app.m.b(Unknown Source:7)
        at android.support.v4.app.m.a(Unknown Source:74)
        at android.support.v4.app.m.onCreateView(Unknown Source:216)
        at android.support.v4.app.j.a(Unknown Source:4)
        at android.support.v4.app.h.a(Unknown Source:2)
        at android.support.v4.app.d.onCreateView(Unknown Source:0)
        at android.support.v4.app.h.onCreateView(Unknown Source:0)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:780)
        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.rInflate(LayoutInflater.java:866)
        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 android.view.LayoutInflater.inflate(LayoutInflater.java:374)
        at android.support.v7.app.AppCompatDelegateImplV9.b(Unknown Source:23)
        at android.support.v7.app.d.setContentView(Unknown Source:4)
        at com.mypackage.myapp.MainActivity.onCreate(Unknown Source:12)
        at android.app.Activity.performCreate(Activity.java:7130)
        at android.app.Activity.performCreate(Activity.java:7121)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1262)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2905)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3060)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:110)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1800)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6649)
        at java.lang.reflect.Method.invoke(Native Method)
    2018-05-16 12:13:14.044 24573-24573/com.mypackage.myapp.x E/AndroidRuntime:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:826)
     Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: com.mypackage.myapp.fragments.MainFragment
        at androidx.navigation.fragment.b$a.a(Unknown Source:58)
        at androidx.navigation.fragment.b$a.a(Unknown Source:19)
        at androidx.navigation.j.a(Unknown Source:16)
        at androidx.navigation.j.a(Unknown Source:133)
        at androidx.navigation.j.a(Unknown Source:31)
            ... 38 more
     Caused by: java.lang.ClassNotFoundException: com.mypackage.myapp.fragments.MainFragment
        at java.lang.Class.classForName(Native Method)
        at java.lang.Class.forName(Class.java:453)
        at androidx.navigation.fragment.b$a.a(Unknown Source:45)
            ... 42 more
     Caused by: java.lang.ClassNotFoundException: Didn't find class "com.mypackage.myapp.fragments.MainFragment" on path: DexPathList[[zip file "/system/framework/org.apache.http.legacy.boot.jar", zip file "/data/app/com.mypackage.myapp.x-ysts055HQTtJTv5J2uej3g==/base.apk"],nativeLibraryDirectories=[/data/app/com.mypackage.myapp.x-ysts055HQTtJTv5J2uej3g==/lib/x86, /system/lib]]
        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:125)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
            ... 45 more

可能是因为 AAPT 还没有为导航组件生成保留规则?

【问题讨论】:

  • 这是一个已知问题,但如果您可以file a bug 以确保在用户可见的位置对其进行跟踪,将会很有帮助
  • @ianhanniballake 这里是它的错误报告,其中包含一个示例项目来重现问题issuetracker.google.com/issues/79874119

标签: android android-proguard android-jetpack android-architecture-navigation


【解决方案1】:

我知道 Proguard 和 R8 应该保留库类的所有子类,但在这种情况下,片段类似乎丢失了。这条保留规则解决了我的问题,但从技术上讲,我们根本不需要这条规则!

-keep class * extends android.support.v4.app.Fragment{}

如果您使用的是 AndroidX,则使用此规则:-keep class * extends androidx.fragment.app.Fragment{}

如果您在导航 XML 中使用 argType,则还需要为引用的类制定规则,例如:-keep class com.example.model.MyModel。或者更好的是,按照official documentation 的建议,将可打包和可序列化的类排除在重命名之外。 -keepnames class * extends android.os.Parcelable -keepnames class * extends java.io.Serializable

【讨论】:

  • 谢谢凯万!我一直在努力解决这个问题:/。我修改了我的 proguard 看起来像这样 -keep class com.example.myapp.** extends android.support.v4.app.Fragment{} 更具体一点
  • 这实际上是让 Fragment 类的子级免受 ProGuard 混淆的非常糟糕的选择。相反,请查看您的片段所需的数据类型/模型,并且永远不要尝试序列化整个片段,因为它是生命周期有界的并且可以有很多不同的状态,但是当前视图(例如片段)的状态必须是由 ViewModel-s 等单独的类存储和处理
  • 感谢您的回答。我尝试只保留导航片段,但它不起作用。你知道也许我需要保留一些依赖的片段吗?我不想保留所有片段的扩展。
  • 是不是太过分了?只需添加-keepnames class androidx.navigation.fragment.NavHostFragment
  • 我遇到了 runtimeException,只需添加 -keepnames class * extends android.os.Parcelable-keepnames class * extends java.io.Serializable 即可修复。
【解决方案2】:

我的问题是我在布局上使用了片段名称。在 R8 之后,名称被混淆导致问题。

<androidx.fragment.app.FragmentContainerView
    android:id="@+id/nav_host_fragment"                
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent""
    app:defaultNavHost="true"
    app:navGraph="@navigation/navigation_home" />

就我而言,解决方案是只保留 Proguard 文件中的名称,如下所示:

#-------------------------------------------------
# JetPack Navigation
# This fixes: Caused by: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment androidx.navigation.fragment.NavHostFragment: make sure class name exists
#-------------------------------------------------
-keepnames class androidx.navigation.fragment.NavHostFragment

【讨论】:

  • 在我尝试测试发布版本时为我节省了很多时间
  • 谢谢@rodrigo.lourenco!正如其他答案所暗示的那样,保留所有 Fragment 类对我来说似乎太核心了,并且使用这个小的保留规则它可以正常工作。你应该得到更多的支持。
  • 可以在 Debug 版本中测试吗?
  • @IgorGanapolsky 我想是的,你可以有一个混淆的调试版本。但是,如果您真的在尝试调试,请做好准备,因为一步一步将无处不在。
【解决方案3】:

确实遇到了同样的问题。上面的答案给了我正确的研究方向。 根据navigation docs

-keep class * extends android.support.v4.app.Fragment{} <-this is not needed 

您需要做的就是保留通过安全参数传递的数据类的名称,例如

-keepnames class com.your.package.models.*

【讨论】:

  • 提问时请提供正确的标签。标签是一个关键字或标签,用于将您的问题与其他类似问题进行分类。使用正确的标签可以让其他人更容易找到并回答您的问题。
  • 如果我通过 Args 传递布尔值怎么办?
【解决方案4】:

自 Android Gradle Plugin 4.1 起已修复此问题。

无需为android:name 属性中定义的片段定义Proguard 规则。

https://issuetracker.google.com/issues/142601969

【讨论】:

    猜你喜欢
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-23
    相关资源
    最近更新 更多