28

When I set minifyEnabled & shrinkResources to true,the Retrofit body JSON request sent is blank, and when it is set to false then it works perfectly fine.

I have posted the whole gradle file so that, you can help me telling what I am doing wrong.

My Retrofit request json body goes like this when minifyEnabled & shrinkResources is true :

{}

It works fine when minifyEnabled & shrinkResources is false:

{"Data":"demoToken","Key":"demokey","Token":"2a9a8677-ac79-49d6-9947-d797b3e4d8e5"}

My gradle looks like this :

   apply {
    plugin 'com.android.application'
    plugin 'kotlin-android'
    plugin 'kotlin-android-extensions'
    plugin 'kotlin-kapt'
    plugin 'io.fabric'
    plugin 'com.google.firebase.firebase-perf'
}
android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.something.theapp"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 43 
        versionName "0.6.7"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        flavorDimensions "server"
        multiDexEnabled true
        vectorDrawables.useSupportLibrary = true
        javaCompileOptions {
            annotationProcessorOptions {
                includeCompileClasspath = true
            }
        }
    }
    signingConfigs {
        kaira {
            storeFile file('key_tts.jks')
            storePassword 'android'
            keyAlias 'tts_key'
            keyPassword 'android'
        }
    }
    buildTypes {
        debug {
            signingConfig signingConfigs.kaira
            minifyEnabled true
            shrinkResources true
            buildConfigField "boolean", "ALLOW_DATABASE", "false"
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        release {
            signingConfig signingConfigs.kaira
            minifyEnabled true
            shrinkResources true
            buildConfigField "boolean", "ALLOW_DATABASE", "false"
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    productFlavors {
        local {
            //buildConfigField "String", "WebServiceUrl", "\"http://192.168.1.136:6264/siteapp/api/\""
//            buildConfigField "String", "WebServiceUrl", "\"http://192.168.1.21:5678/api/\""
            //   buildConfigField "String", "WebServiceUrl", "\"http://192.168.1.233:5677/api/\""
            //  buildConfigField "String", "WebServiceUrl", "\"http://192.168.1.26:5678/api/\""
            //buildConfigField "String", "WebServiceUrl", "\"http://192.168.1.25:5678/api/\""
//            buildConfigField "String", "WebServiceUrl", "\"http://192.168.1.37:5678/api/\""
//            buildConfigField "String", "WebServiceUrl", "\"https://api.forsell.in/api/\""
            //  buildConfigField "String", "WebServiceUrl", "\"http://192.168.1.23:5678/api/\""
            buildConfigField "String", "WebServiceUrl", "\"http://192.168.1.29:5678/api/\""
            buildConfigField "String", "WebUrl", "\"https://someurl.in/\""
            buildConfigField "String", "ServerName", "\"-Local\""
            dimension "server"
            copy {
                from "src/local"
                include "google-services.json"
                into "."
            }
        }
        temp {
            buildConfigField "String", "WebServiceUrl", "\"http://api.lezza.in/api/\""
            buildConfigField "String", "WebUrl", "\"https://someurl.in/\""
            buildConfigField "String", "ServerName", "\"-Local\""
            dimension "server"
            copy {
                from "src/local"
                include "google-services.json"
                into "."
            }
        }
        live {
            buildConfigField "String", "WebServiceUrl", "\"https://api.someurl.in/api/\""
            buildConfigField "String", "WebUrl", "\"https://someurl.in/\""
            buildConfigField "String", "ExtClientNameNew", "\"ExtClientName:TTS\""
            dimension "server"
            copy {
                from "src/livegcm"
                include "google-services.json"
                into "."
            }
        }
        staging {
            //buildConfigField "String", "WebServiceUrl", "\"http://192.168.1.233:85/api/\""//sunil
            //     buildConfigField "String", "WebServiceUrl", "\"http://192.168.1.233:5677/api/\""//devang
            //   buildConfigField "String", "WebServiceUrl", "\"http://192.168.1.233:85/api/\""
            buildConfigField "String", "WebServiceUrl", "\"https://stageapi.someurl.in/api/\""
            buildConfigField "String", "WebUrl", "\"https://stageweb.someurl.in/\""
            buildConfigField "String", "ServerName", "\"-Staging\""
            dimension "server"
            copy {
                from "src/local"
                include "google-services.json"
                into "."
            }
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }
    dexOptions {
        preDexLibraries = false
        javaMaxHeapSize "4g" // 2g should be also OK
    }
    defaultConfig {
        vectorDrawables.useSupportLibrary = true
    }
    lintOptions {
        abortOnError false
        disable 'MissingTranslation'
    }
}
repositories {
    mavenCentral()
    maven { url 'https://maven.fabric.io/public' }
}

configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        def requested = details.requested
        if (requested.group == 'com.android.support') {
            if (!requested.name.startsWith("multidex")) {
                details.useVersion '28.0.0'
            }
        }
    }
}
dependencies {
    def lifecycle_version = "2.0.0"
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'androidx.core:core-ktx:1.0.2'
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.vectordrawable:vectordrawable:1.0.0-alpha1'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.2') {
        exclude group: 'com.google.code.findbugs'
    }
    implementation 'com.squareup.retrofit2:retrofit:2.5.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    implementation 'com.squareup.okhttp3:okhttp:3.12.1'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
    implementation 'com.google.android.material:material:1.0.0'
    implementation 'androidx.recyclerview:recyclerview:1.0.0'
    implementation 'com.github.bumptech.glide:glide:4.5.0'
    implementation 'com.karumi:dexter:5.0.0'
    implementation 'com.google.android.gms:play-services-maps:16.1.0'
    implementation 'com.google.android.gms:play-services-location:16.0.0'
    implementation 'com.google.android.gms:play-services-auth:16.0.1'
    implementation 'org.jetbrains.anko:anko-common:0.9'
    implementation 'com.google.android.gms:play-services-ads:17.2.0'
    implementation 'androidx.multidex:multidex:2.0.1'
    implementation project(path: ':imagepicker')
    implementation 'androidx.percentlayout:percentlayout:1.0.0'
    implementation 'com.google.firebase:firebase-core:16.0.9'
    implementation 'com.google.firebase:firebase-inappmessaging-display:17.2.0'
    implementation 'com.google.firebase:firebase-messaging:18.0.0'
    implementation 'com.google.firebase:firebase-config:17.0.0'
    implementation 'com.google.firebase:firebase-perf:17.0.2'
    implementation 'com.github.chrisbanes:PhotoView:2.3.0'
    implementation 'com.google.android.gms:play-services-places:16.1.0'
    implementation 'com.google.android.libraries.places:places:1.1.0'
    api 'com.theartofdev.edmodo:android-image-cropper:2.8.+'
    implementation 'com.github.faruktoptas:RetrofitRssConverterFactory:0.1.0'
    implementation 'com.wang.avi:library:2.1.3'
    implementation 'com.facebook.android:facebook-android-sdk:5.4.0'
    implementation('com.crashlytics.sdk.android:crashlytics:2.9.8@aar') {
        transitive = true
    }
    implementation 'com.haozhang.libary:android-slanted-textview:1.2'
    implementation 'com.facebook.shimmer:shimmer:0.4.0'
    implementation 'com.github.freshdesk:freshchat-android:1.5.3'
    implementation 'commons-io:commons-io:2.4'
    implementation 'com.getkeepsafe.taptargetview:taptargetview:1.12.0'
    implementation 'com.razorpay:checkout:1.5.6'
    implementation 'com.google.android.gms:play-services-analytics:17.0.0'

}
apply plugin: 'com.google.gms.google-services'

Below is my Main model class

class ServiceRequest {

    var Key: String? = "SiteAdminAppkey"
    var Token: String? = ""
    //    var Slug: String? = "admin"
    var Data: Any? = null
}

I am able to get this working by adding @SerializeName annotation but I will have to do it in every model class. In one of my other project, Proguard and everything works without serialization.

Salman Shaikh
  • 589
  • 6
  • 19
  • Can you add your models and how you serializer them please? – Fred Aug 30 '19 at 05:34
  • it cant be the case as shrink resources and minify enable has nothing to do with json – Shivam Oberoi Aug 30 '19 at 05:36
  • @ShivamOberoi It's Retrofit request body – Salman Shaikh Aug 30 '19 at 06:03
  • @Fred I don't serialize it. It works fine even without it. In other projects, i don't face this issue. – Salman Shaikh Aug 30 '19 at 06:05
  • i have same code and its working fine with retrofit and minify enabled – Shivam Oberoi Aug 30 '19 at 06:06
  • 1
    Minify enabled might affect json serialization. If you forgot to add annotations to serialize the attributes with specific names, they'll be serialized with obfuscated names. Maybe, they are even being stripped from the code and that's why you don't see them. I was asking for the models to see how you've defined them and it would be nice to check the proguard config too. – Fred Aug 30 '19 at 06:18
  • have you write your pro-guard rules properly, might be the error is due to pro-guard rules – Kailash Chouhan Aug 30 '19 at 06:51
  • If you are using androidx, then you must use R8. After the migration, I also had to update the proguard rules. Check this for further info: https://r8.googlesource.com/r8/+/refs/heads/master/compatibility-faq.md – cylon Aug 30 '19 at 07:25

8 Answers8

28

Add below line in your proguard-rules.pro File

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

Also add below line in gradle.properties file.

android.enableR8=false

I face same issue and added above line in the file and its working for me.

Mehul Kabaria
  • 6,404
  • 4
  • 25
  • 50
11

Add below line in proguard-rules

-keepclassmembers class <yourpackagename>.** { <fields>; }

replace yourpackagename with the directory path where all your model classes , like in my case

-keepclassmembers class com.demo.app.model.** { <fields>; }
Kailash Chouhan
  • 2,328
  • 16
  • 16
  • You really saved my time. I have searched a lot, but your solution worked for me. Thanks man!!! – Shailesh Jul 15 '21 at 13:40
  • Yeah, it is the best answer you need, because when we manifyEnabled true, the fields member become unreadable and become different from API request that we expect – M Moersalin Sep 12 '21 at 15:49
8

This is what worked for me.

# Retrofit2
-keepclasseswithmembers class * {
    @retrofit2.http.* <methods>;
}
-keepclassmembernames interface * {
    @retrofit2.http.* <methods>;
}

# GSON Annotations
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}

For some reason, it didn't work without the GSON annotations settings, even when adding a @Keep annotation on the model class directly.

Niko Wah
  • 131
  • 1
  • 3
4

Since you're using Gson to convert Json to you model, you need to make sure that reflections are working properly on these classes (as I assume you haven't created a custom adapter for these).

The easiest approach to keep the class and field names, you could just annotate the model with @Keep from AndroidX Annotations:

Denotes that the annotated element should not be removed when the code is minified at build time. This is typically used on methods and classes that are accessed only via reflection so a compiler may think that the code is unused.

tynn
  • 38,113
  • 8
  • 108
  • 143
  • It's about the members of your model. _AndroidX_ is only relevant insofar it provides the `@Keep` annotation. If you provide your model, I'll be able to provide an example. – tynn Aug 30 '19 at 07:06
1

This is an updated answer(05/20), because nothing seems to work in my case.

  1. Use the Proguard rules from Retrofit docs here.

  2. Keep your model class like Chouhan answer.

noe
  • 794
  • 8
  • 13
1

Above answers all apply to gson. But I experienced the same problem using kotlinx serialization.

This calls for a different set of entries into the proguard-rules as found here in the kotlinx serialization documentation.

# Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
    static <1>$Companion Companion;
}

# Keep `serializer()` on companion objects (both default and named) of serializable classes.
-if @kotlinx.serialization.Serializable class ** {
    static **$* *;
}
-keepclassmembers class <2>$<3> {
    kotlinx.serialization.KSerializer serializer(...);
}

# Keep `INSTANCE.serializer()` of serializable objects.
-if @kotlinx.serialization.Serializable class ** {
    public static ** INSTANCE;
}
-keepclassmembers class <1> {
    public static <1> INSTANCE;
    kotlinx.serialization.KSerializer serializer(...);
}

# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault

# Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`.
# If you have any, uncomment and replace classes with those containing named companion objects.
#-keepattributes InnerClasses # Needed for `getDeclaredClasses`.
#-if @kotlinx.serialization.Serializable class
#com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions.
#com.example.myapplication.HasNamedCompanion2
#{
#    static **$* *;
#}
#-keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept.
#    static <1>$$serializer INSTANCE;
#}
FFFF0H
  • 658
  • 8
  • 11
0
  • Use latest version of retrofit lib: com.squareup.retrofit2:retrofit:2.7.1 If you are using R8 the shrinking and obfuscation rules are included automatically. Latest Retrofit lib

  • Also, Change in Gson implementation 'com.google.code.gson:gson:2.8.6' Latest GSON lib

  • Check the R8 compatibility-faq r8-compatibility-faq for GSON

  • In case your are using OkHttp3 then change to the latest version okhttp3LibVersion = '4.3.1

0

For my case, this was the solution:

# For GSON annotation
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}
Mafujul
  • 1,090
  • 10
  • 15