Фатальное исключение: java.lang.NullPointerException в сборке выпуска

Я столкнулся со странной проблемой в сборке релиза приложения. Вот мое исключение

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)

Файл Pojo

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*

2 ответа

Решение

Наконец-то решили эту проблему. Это из-за новой обфускации кода 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 не сработало.

Отключение R8совсем не может быть хорошей идеей. Но добавление следующих строк в файл правил Proguard может решить проблему -

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

Это также предлагается одним из разработчиков Google в качестве желаемого решения. Вы можете найти его ответ здесь, а также можете следить за всем обсуждением вокруг него.

Кажется, GSON и R8 плохо работают вместе, и об этом написано здесь:

На самом деле, следующее также должно работать:

-kekeclassmembers,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  ----------

Если аннотация @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

Возможно, вам придется использовать

-keepclassmembers enum * { *; }

сохранить ваши перечисления

В моем случае добавление android.enableR8=true в мои gradle.properties и -keepclassmembers,allowobfuscation class * { @com.google.gson.annotations.SerializedName <fields>; } с моим proguard-rules.pro сделали свое дело

Я полагаю, что вы получаете нуль в поле JSON min_allowed_version из-за исключения четко набирается:

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

Это означает, что когда вы звоните minAllowedVersion поле и его возвращение 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
)

С помощью @Keep аннотации могут быть разумной альтернативой для работы с файлами proguard. Просто аннотируйте свой класс данных с помощью@Keep и весь класс с его членами не будет минифицирован.

Это особенно полезно, когда имена всех свойств совпадают с именами полей json и нет необходимости аннотировать свойства с помощью @SerializedName. Правила Proguard для занятий с@SerializedName аннотированные поля (упомянутые в других ответах) не применимы в таком случае.

Другие вопросы по тегам