Preference 中的硬编码 app:fragment 字符串在 Release 中崩溃
Hardcoded app:fragment string in Preference crashes in Release
下面是我的设置屏幕的 xml 文件的简化版本。
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--hardcoded app:fragment crashes in release because of ClassNameNotFound -->
<Preference
app:allowDividerAbove="true"
app:fragment="com.flamyoad.tsukiviewer.ui.settings.preferences.ClearDataPreferences"
app:icon="@drawable/ic_delete_settings_24dp"
app:title="Clear data" />
</androidx.preference.PreferenceScreen>
它在调试版本中运行良好。但是当我点击发布 apk 的首选项时,它会因为 ClassNotFoundException
.
而崩溃
堆栈跟踪在这里
Process: com.flamyoad.tsukiviewer, PID: 13647
androidx.fragment.app.Fragment$c: Unable to instantiate fragment com.flamyoad.tsukiviewer.ui.settings.preferences.ClearDataPreferences: make sure class name exists
at i.l.d.n.d()
at androidx.fragment.app.Fragment.a()
at i.l.d.r$c.a(:2)
at com.flamyoad.tsukiviewer.ui.settings.SettingsActivity.a(:2)
at i.s.f.b(:29)
at androidx.preference.Preference.a(:23)
at androidx.preference.Preference$a.onClick()
at android.view.View.performClick(View.java:4757)
at android.view.View$PerformClick.run(View.java:19769)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5289)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Caused by: java.lang.ClassNotFoundException: com.flamyoad.tsukiviewer.ui.settings.preferences.ClearDataPreferences
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:308)
at i.l.d.n.c(:2)
at i.l.d.n.d()
at androidx.fragment.app.Fragment.a()
at i.l.d.r$c.a(:2)
at com.flamyoad.tsukiviewer.ui.settings.SettingsActivity.a(:2)
at i.s.f.b(:29)
at androidx.preference.Preference.a(:23)
at androidx.preference.Preference$a.onClick()
at android.view.View.performClick(View.java:4757)
at android.view.View$PerformClick.run(View.java:19769)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5289)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.flamyoad.tsukiviewer.ui.settings.preferences.ClearDataPreferences" on path: DexPathList[[zip file "/data/app/com.flamyoad.tsukiviewer-1/base.apk"],nativeLibraryDirectories=[/vendor/lib64, /system/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:308)
at i.l.d.n.c(:2)
at i.l.d.n.d()
at androidx.fragment.app.Fragment.a()
at i.l.d.r$c.a(:2)
at com.flamyoad.tsukiviewer.ui.settings.SettingsActivity.a(:2)
at i.s.f.b(:29)
at androidx.preference.Preference.a(:23)
at androidx.preference.Preference$a.onClick()
at android.view.View.performClick(View.java:4757)
at android.view.View$PerformClick.run(View.java:19769)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5289)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Suppressed: java.lang.ClassNotFoundException: com.flamyoad.tsukiviewer.ui.settings.preferences.ClearDataPreferences
at java.lang.Class.classForName(Native Method)
at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
... 21 more
Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available
对我来说,看起来原因是 apk 被缩小并且 class 名称被更短的字符串替换。在这种情况下我能做什么?用 Activity
中的函数调用替换 app:fragment
是最佳做法吗?或者还有其他方法吗?
我正在为我的应用程序使用 PreferenceFragmentCompat
。
aapt2 generated proguard rules for preferences 专门查找 android:fragment
属性,而不是 app:fragment
。您希望使用 android:fragment
自动保留偏好片段。
我将这些行添加到我的 proguard-rules.pro
中,它不再崩溃了。
-keep public class * extends androidx.preference.Preference
-keep public class * extends androidx.preference.PreferenceFragmentCompat
下面是我的设置屏幕的 xml 文件的简化版本。
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--hardcoded app:fragment crashes in release because of ClassNameNotFound -->
<Preference
app:allowDividerAbove="true"
app:fragment="com.flamyoad.tsukiviewer.ui.settings.preferences.ClearDataPreferences"
app:icon="@drawable/ic_delete_settings_24dp"
app:title="Clear data" />
</androidx.preference.PreferenceScreen>
它在调试版本中运行良好。但是当我点击发布 apk 的首选项时,它会因为 ClassNotFoundException
.
堆栈跟踪在这里
Process: com.flamyoad.tsukiviewer, PID: 13647
androidx.fragment.app.Fragment$c: Unable to instantiate fragment com.flamyoad.tsukiviewer.ui.settings.preferences.ClearDataPreferences: make sure class name exists
at i.l.d.n.d()
at androidx.fragment.app.Fragment.a()
at i.l.d.r$c.a(:2)
at com.flamyoad.tsukiviewer.ui.settings.SettingsActivity.a(:2)
at i.s.f.b(:29)
at androidx.preference.Preference.a(:23)
at androidx.preference.Preference$a.onClick()
at android.view.View.performClick(View.java:4757)
at android.view.View$PerformClick.run(View.java:19769)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5289)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Caused by: java.lang.ClassNotFoundException: com.flamyoad.tsukiviewer.ui.settings.preferences.ClearDataPreferences
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:308)
at i.l.d.n.c(:2)
at i.l.d.n.d()
at androidx.fragment.app.Fragment.a()
at i.l.d.r$c.a(:2)
at com.flamyoad.tsukiviewer.ui.settings.SettingsActivity.a(:2)
at i.s.f.b(:29)
at androidx.preference.Preference.a(:23)
at androidx.preference.Preference$a.onClick()
at android.view.View.performClick(View.java:4757)
at android.view.View$PerformClick.run(View.java:19769)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5289)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.flamyoad.tsukiviewer.ui.settings.preferences.ClearDataPreferences" on path: DexPathList[[zip file "/data/app/com.flamyoad.tsukiviewer-1/base.apk"],nativeLibraryDirectories=[/vendor/lib64, /system/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:308)
at i.l.d.n.c(:2)
at i.l.d.n.d()
at androidx.fragment.app.Fragment.a()
at i.l.d.r$c.a(:2)
at com.flamyoad.tsukiviewer.ui.settings.SettingsActivity.a(:2)
at i.s.f.b(:29)
at androidx.preference.Preference.a(:23)
at androidx.preference.Preference$a.onClick()
at android.view.View.performClick(View.java:4757)
at android.view.View$PerformClick.run(View.java:19769)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5289)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Suppressed: java.lang.ClassNotFoundException: com.flamyoad.tsukiviewer.ui.settings.preferences.ClearDataPreferences
at java.lang.Class.classForName(Native Method)
at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
... 21 more
Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available
对我来说,看起来原因是 apk 被缩小并且 class 名称被更短的字符串替换。在这种情况下我能做什么?用 Activity
中的函数调用替换 app:fragment
是最佳做法吗?或者还有其他方法吗?
我正在为我的应用程序使用 PreferenceFragmentCompat
。
aapt2 generated proguard rules for preferences 专门查找 android:fragment
属性,而不是 app:fragment
。您希望使用 android:fragment
自动保留偏好片段。
我将这些行添加到我的 proguard-rules.pro
中,它不再崩溃了。
-keep public class * extends androidx.preference.Preference
-keep public class * extends androidx.preference.PreferenceFragmentCompat