13
votes

Moshi KotlinJsonAdapterFactory ne peut pas analyser Json après avoir activé minify

J'ai développé une application Android, en utilisant Moshi comme l'une de ses dépendances.

Aujourd'hui, je souhaite activer minify pour ce projet. J'ai donc défini minifyEnabled true dans mon build.gradle .

Après cela, j'ai constaté que toutes les réponses du serveur devenaient nulles.

Tout d'abord, j'utilise Retrofit2 pour appeler des API. Le corps JSON dans Response.body() n'est pas nul et a des valeurs correctes.

Le corps de la réponse est comme ci-dessous (simplifié):

# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
   public *;
}

# Uncomment this to preserve the line number information for
# debugging stack traces.
-keepattributes SourceFile,LineNumberTable, *Annotation*

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile


#Crashlytics: https://docs.fabric.io/android/crashlytics/dex-and-proguard.html
-keepattributes *Annotation*
-keep public class * extends java.lang.Exception


#https://stackoverflow.com/questions/36816521/is-the-format-of-the-data-held-in-kotlin-metadata-documented-anywhere
-dontwarn kotlin.**
-dontwarn kotlin.reflect.jvm.internal.**
-keep class kotlin.reflect.jvm.internal.** { *; }
-keep class kotlin.Metadata { *; }
-keepclassmembers public class com.example.app.** {
    public synthetic <methods>;
}
-keepclassmembers class kotlin.Metadata {
    public <methods>;
}
-keepclassmembers class **$WhenMappings {
    <fields>;
}
-keep class kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoaderImpl

#All models
-keep class com.example.app.models.**

#######Retrofit#######
#https://github.com/square/retrofit/blob/master/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro
# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
# EnclosingMethod is required to use InnerClasses.
-keepattributes Signature, InnerClasses, EnclosingMethod

# Retrofit does reflection on method and parameter annotations.
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations

# Retain service method parameters when optimizing.
-keepclassmembers,allowshrinking,allowobfuscation interface * {
    @retrofit2.http.* <methods>;
}

# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

# Ignore JSR 305 annotations for embedding nullability information.
-dontwarn javax.annotation.**

# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
-dontwarn kotlin.Unit

# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.KotlinExtensions

# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
-if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface <1>



#######OkHttp3######
#https://github.com/square/okhttp/blob/master/okhttp/src/main/resources/META-INF/proguard/okhttp3.pro
# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**

# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase

# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*

# OkHttp platform used only on JVM and when Conscrypt dependency is available.
-dontwarn okhttp3.internal.platform.ConscryptPlatform




######Okio######
#https://github.com/square/okio/blob/master/okio/src/jvmMain/resources/META-INF/proguard/okio.pro
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*





####Moshi####
#https://github.com/square/moshi/blob/master/moshi/src/main/resources/META-INF/proguard/moshi.pro
#https://github.com/square/moshi/blob/master/kotlin/reflect/src/main/resources/META-INF/proguard/moshi-kotlin.pro
# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**

-keepclasseswithmembers class * {
    @com.squareup.moshi.* <methods>;
}

-keep @com.squareup.moshi.JsonQualifier interface *

# Enum field names are used by the integrated EnumJsonAdapter.
# Annotate enums with @JsonClass(generateAdapter = false) to use them with Moshi.
-keepclassmembers @com.squareup.moshi.JsonClass class * extends java.lang.Enum {
    <fields>;
}

# The name of @JsonClass types is used to look up the generated adapter.
-keepnames @com.squareup.moshi.JsonClass class *

# Retain generated JsonAdapters if annotated type is retained.
-if @com.squareup.moshi.JsonClass class *
-keep class <1>JsonAdapter {
    <init>(...);
    <fields>;
}
-if @com.squareup.moshi.JsonClass class **$*
-keep class <1>_<2>JsonAdapter {
    <init>(...);
    <fields>;
}
-if @com.squareup.moshi.JsonClass class **$*$*
-keep class <1>_<2>_<3>JsonAdapter {
    <init>(...);
    <fields>;
}
-if @com.squareup.moshi.JsonClass class **$*$*$*
-keep class <1>_<2>_<3>_<4>JsonAdapter {
    <init>(...);
    <fields>;
}
-if @com.squareup.moshi.JsonClass class **$*$*$*$*
-keep class <1>_<2>_<3>_<4>_<5>JsonAdapter {
    <init>(...);
    <fields>;
}
-if @com.squareup.moshi.JsonClass class **$*$*$*$*$*
-keep class <1>_<2>_<3>_<4>_<5>_<6>JsonAdapter {
    <init>(...);
    <fields>;
}
-keep class kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoaderImpl

-keepclassmembers class kotlin.Metadata {
    public <methods>;
}






####Mp4 Marser####
-keep class com.coremedia.iso.** {*;}
-keep class com.googlecode.mp4parser.** {*;}
-keep class com.mp4parser.** {*;}

-dontwarn com.coremedia.**
-dontwarn com.googlecode.mp4parser.**



####Picasso#####
#https://github.com/square/picasso/blob/master/picasso/consumer-proguard-rules.txt
# Platform calls Class.forName on types which do not exist on Android to determine platform.
-dontnote okhttp3.internal.Platform
# java.nio.file.* usage which cannot be used at runtime. Animal sniffer annotation.
-dontwarn okio.Okio
# JDK 7-only method which is @hide on Android. Animal sniffer annotation.
-dontwarn okio.DeflaterSink



#Ignore all other 3rd party libraries for now as we don't really care about the size but more about code obfuscation.
-keep class de.hdodenhof.**
-keep class io.github.luizgrp.sectionedrecyclerviewadapter.**
-keep class q.rorbin.badgeview.**
-keep class com.theartofdev.edmodo.**
-keep class me.relex
-keep class com.tbruyelle.rxpermissions2.**
-keep class com.github.pwittchen.reactivenetwork.**
-keep class com.minimize.android.rxrecycleradapter.**
-keep class at.blogc.android.**
-keep class com.yarolegovich.**
-keep class cn.trinea.android.view.autoscrollviewpager.**
-keep class com.apkfuns.logutils.**

Et j'utilise le code ci-dessous pour le convertir en mon propre objet:

class SomeResponse {
    @Json(name="status")
    var status: String? = null

    @Json(name="data")
    var data: User? = null
}

Alors que le code pour SomeResponse :

val someResponse = Moshi.Builder().add(KotlinJsonAdapterFactory()).build().adapter(SomeResponse::class.java).fromJson(theJsonString)

Et puis j'utilise simplement Log.i("Moshi", "${someResponse.status}" pour voir la valeur, et le résultat est null .

J'ai déjà inclus les règles proguard spécifiées dans la section README de Moshi Github, qui est celle-ci et celle-ci .

Pourquoi et comment résoudre ce problème?

Pour référence, proguard-rules.pro mon proguard-rules.pro complet:

{"status":"success","data":{"user": "I am a user"}}


9 commentaires

Le readme a une section pour proguard (vers le bas) sur GitHub: github.com/square/moshi/blob/master/README.md avez-vous implémenté cela?


@MarkKeen Oui, je viens de mettre à jour ma question. Merci quand même


Conservez l'annotation des métadonnées de Kotlin. C'est tout ce qui devrait être nécessaire. Jake Wharton


@coroutineDispatcher J'ai cherché sur Google et essayé la solution ici: medium.com/@AthorNZ/ ... Mais je le résultat est le même. Est-ce que cet article est ce que vous voulez dire?


@SiraLam quel est le package de SomeResponse ?


@BartekLipinski com.example.app.models


Je soupçonne votre règle pour le modèle. Pouvez-vous essayer d'utiliser -keep class com.example.app.models.** { *; } ?


J'ai eu un problème pour obtenir une valeur nulle lors de l'analyse de json avec moshi dans kotlin. Je l'ai résolu en utilisant @field: Json (name = "") au lieu de @Json (name = ""). Voyez si cela aide.


@BartekLipinski Désolé, j'étais en vacances la semaine dernière. J'ai juste essayé d'ajouter { *; } , mais pas de chance.


5 Réponses :


0
votes

Je pense que la solution ici est simple: ajoutez un @Keep - Anotation à votre modèle (SomeResponse), ainsi les noms de marshalling ne devraient plus être obscurcis. :-)


0 commentaires

0
votes

Essayez de l'ajouter à votre fichier proguard-rules.pro. J'espère que cela aidera.

# Moshi
-keepclassmembers class ** {
  @com.squareup.moshi.FromJson *;
  @com.squareup.moshi.ToJson *;
}


0 commentaires

3
votes
-keepclasseswithmembers class * {
    @com.squareup.moshi.* <methods>;
}
-keep @com.squareup.moshi.JsonQualifier interface *
-dontwarn org.jetbrains.annotations.**
-keep class kotlin.Metadata { *; }
-keepclassmembers class kotlin.Metadata {
    public <methods>;
}

-keepclassmembers class * {
    @com.squareup.moshi.FromJson <methods>;
    @com.squareup.moshi.ToJson <methods>;
}

-keepnames @kotlin.Metadata class (Change with Yourpackagename) com.myapp.packagename.model.**
-keep class (Change with Yourpackagename) com.myapp.packagnename.model.** { *; }
-keepclassmembers class (Change with Yourpackagename) com.myapp.packagename.model.** { *; }
Change com.myapp.packagnename to your packagename ORReplace moshi-kotlin with kotshi which plays fine with Proguard without special rules, dropping hundreds of kilobytes in the final apk.

0 commentaires

7
votes

La seule solution qui a fonctionné pour moi est mentionnée dans le commentaire de @JeganBabu

ie Changer @Json(name="field_name") en @field:Json(name="field_name") .

La raison est probablement que l'annotation n'est pas appliquée au code Java converti si le field: n'a pas été ajouté. Assez bizarre.

Pour votre référence: (Moshi in kotlin) @Json vs @field: Json


0 commentaires

2
votes

Vous pouvez également utiliser le processeur d'annotations Moshi "codegen".

Ajoutez la dépendance:

@JsonClass(generateAdapter = false)
enum class MyEnumClass { ... }

Puis @JsonClass vos modèles avec @JsonClass :

@JsonClass(generateAdapter = true)
data class MyModel(...)

Vous devez également annoter toutes les classes enum utilisées par les modèles, mais elles n'ont pas besoin d'adaptateur:

kapt "com.squareup.moshi:moshi-kotlin-codegen:1.9.2"

Les règles Moshi Proguard précédemment mentionnées ici et ici sont également requises.


0 commentaires