发布版本中的致命异常:java.lang.NullPointerException

30

我在应用程序的发布版中遇到了一个奇怪的问题。以下是我的异常信息:

Fatal Exception: java.lang.NullPointerException`
throw with null exception
in.hopq.hopq.authentication.models.AppUpdateSourceDO$AppUpdate.getMinAllowedVersion (AppUpdateSourceDO.java:3)
in.hopq.hopq.authentication.activities.SplashActivity$onCreate$1.onChanged (SplashActivity.java:48)
in.hopq.hopq.authentication.activities.SplashActivity$onCreate$1.onChanged (SplashActivity.java:31)

普通Java对象文件

data class AppUpdateSourceDO(
    @SerializedName("app_update")
    val appUpdate: AppUpdate,
    @SerializedName("message")
    val message: String,
    @SerializedName("success")
    val success: Boolean
) {
data class AppUpdate(
        @SerializedName("excluded_versions")
        val excludedVersions: List<ExcludedVersion>,
        @SerializedName("min_allowed_version")
        val minAllowedVersion: Int,
        @SerializedName("min_allowed_version_ios")
        val minAllowedVersionIos: String,
        @SerializedName("recommended_version")
        val recommendedVersion: Int?
) {
    data class ExcludedVersion(
            @SerializedName("version")
            val version: String
    )
}
}

这是我的proguard文件

##OKHTTP3
-keepattributes Signature
-keepattributes *Annotation*
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
-dontnote okhttp3.**
-dontwarn okio.**
-dontwarn retrofit2.Platform$Java8
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# For using GSON @Expose annotation
-keepattributes *Annotation*

在您的所有POJO或Model类中添加@Keep注解以防止此问题发生。 - M DEV
8个回答

29

终于解决了这个问题。这是由于新的R8代码混淆引起的。只需通过将以下内容添加到gradle.properties文件中来禁用它:

android.enableR8=false

另外,您还可以将以下内容添加到您的proguard规则文件中。

# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}

然而,将此添加到proguard中并没有真正起作用。


2
似乎 R8 仍然存在这个 bug https://issuetracker.google.com/issues/120277396 希望他们能尽快修复。 - Markymark
@MehulKabaria 很高兴 - pratham kesarkar
这对于代码混淆有好处吗?还是会有影响? - Bajrang Hudda
1
很棒的回答,kesarkar先生,但是enableR8是什么? - Gundu Bandgar
@GunduBandgar R8是谷歌的新代码混淆引擎,类似于Proguard。 - pratham kesarkar

28

完全禁用 R8 可能不是一个好主意。但是通过在您的 Proguard Rules 文件中添加以下行可能会解决问题--

-keep class com.example.myapp.MyClass { *; }
-dontwarn com.example.myapp.MyClass
-keepclassmembers,allowobfuscation class * {
    @com.google.gson.annotations.SerializedName <fields>;
  }
-keep,allowobfuscation @interface com.google.gson.annotations.SerializedName

谷歌开发者之一也建议这样做。 你可以在这里找到他的回答,并且你还可以跟随整个讨论。


这个解决方案对我很有效。我实施了这篇文章的第一个解决方案,但在构建和签名发布应用程序时,我遇到了Dagger和OKHttp3的编译问题。 - Johana Lopez 1327

10

看起来GSON和R8不太兼容,在这里有相关说明:

实际上,以下内容也可以工作:

-keepclassmembers,allowobfuscation class * { @com.google.gson.annotations.SerializedName ; } -keep,allowobfuscation @interface com.google.gson.annotations.SerializedName

这将使字段和属性的名称被缩小(混淆),以进一步减少最终APK的大小。

此外,您可以在Gson示例中看到下一个规则:

##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { <fields>; }

# Prevent proguard from stripping interface information from TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}

##---------------End: proguard configuration for Gson  ----------

3
如果在数据类中一致使用@SerializedName注释,则可以使用以下保留规则。
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
 }

如果没有使用@SerializedName注解,则可以为每个数据类使用以下保守规则:
-keepclassmembers class MyDataClass {
  !transient <fields>;
 }

更多信息,请查看以下链接:

https://r8.googlesource.com/r8/+/refs/heads/master/compatibility-faq.md


2
在我的情况下,将android.enableR8=true添加到我的gradle.properties文件中,并在我的proguard-rules.pro文件中添加-keepclassmembers,allowobfuscation class * { @com.google.gson.annotations.SerializedName <fields>; }就解决了问题。

2

您可能需要使用

-keepclassmembers enum * { *; }

保留您的枚举(enums)。

0

我猜想,你在 json 字段 min_allowed_version 中收到 null 是因为异常很明显地被定义了:

Fatal Exception: java.lang.NullPointerException throw with null 
exceptionin.hopq.hopq.authentication.models.AppUpdateSourceDO$AppUpdate.getMinAllowedVersion (AppUpdateSourceDO.java:3)

这意味着,当您调用minAllowedVersion字段的getter并返回null时,您会捕获NPE。尝试使用空安全性,也许一切都会正常工作。
data class AppUpdate(
        @SerializedName("excluded_versions")
        val excludedVersions: List<ExcludedVersion> = listOf(),
        @SerializedName("min_allowed_version")
        val minAllowedVersion: Int? = null,
        @SerializedName("min_allowed_version_ios")
        val minAllowedVersionIos: String? = null,
        @SerializedName("recommended_version")
        val recommendedVersion: Int? = null
)

0

使用@Keep注释可以是处理proguard文件的合理替代方案。只需使用@Keep注释您的数据类,整个类及其成员将不会被缩小。

当所有属性的名称与json字段的名称匹配且不需要使用@SerializedName注释属性时,它尤其有用。对于带有@SerializedName注释字段的类的Proguard规则(在其他答案中提到)不适用于这种情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接