Once I enabled proguard i am not unable to convert JSON string in the POJO class with help of gson library, I am getting null value but it's working fine when proguard is disabled
Log.d(TAG, "JSON: ${StringUtils.data}")
try {
val quickReplyButton = Gson().fromJson(StringUtils.data, QuickReplyButton::class.java)
Log.e(TAG, "quickReplyButtondata:" +quickReplyButton)
}
catch (e: Exception){
e.printStackTrace()
}
Pojo class
package com.mypackage.sdk.data.model
data class QuickReplyButton(
val nodes: List<Node>,
val showMessageText: Boolean,
val transitions: List<Transition>,
val widgetAction: String,
val widgetType: String,
val widgetView: String
)
data class Node(
val controls: Controls,
val id: String
)
data class Transition(
val from: String,
val name: String,
val to: To,
val trigger: String
)
data class Controls(
val `0`: X0
)
data class X0(
val event: Event,
val id: String,
val text: List<String>,
val type: String,
val values: List<String>
)
data class Context(
val border: String,
val color: String,
val textColor: String,
val textStyle: String
)
data class Event(
val name: String
)
data class To(
val sendMessage: SendMessage
)
data class SendMessage(
val displayText: String,
val nvaaId: String,
val nvaaType: String,
val selected: String,
val selectedText: String,
val selectedValue: String
)
Progurd rules:
-keep class com.mypackage.sdk.data.model.QuickReplyButton { *; }
-keep class com.mypackage.sdk.data.model.SendMessage { *; }
-keep class com.mypackage.sdk.data.model.To { *; }
-keep class com.mypackage.sdk.data.model.Event { *; }
-keep class com.mypackage.sdk.data.model.Context { *; }
-keep class com.mypackage.sdk.data.model.X0 { *; }
-keep class com.mypackage.sdk.data.model.Controls { *; }
-keep class com.mypackage.sdk.data.model.Transition { *; }
-keep class com.mypackage.sdk.data.model.Node { *; }
-keep class com.mypackage.sdk.** { *; }
-keep class com.mypackage.sdk.ui.** { *; }
-keep class com.mypackage.sdk.Utils.** { *; }
-keep class com.mypackage.sdk.Care.** { *; }
-keep class com.mypackage.sdk.CareApplication.** { *; }
##---------------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.** { *; }
-keep class com.google.gson.**{ *; }
-dontwarn java.lang.reflect.**
-keep class kotlin.** { *; }
-keepattributes InnerClasses
-keepattributes EnclosingMethod
You need to add "SerializedName" annotation to all of properties.
#SerializedName("data")
#Expose
var data: Data? = null
Related
The project working well without enabling proguard, the problem comes when enable it, I am using retrofit with Moshi converter and Coroutines to fetch list of data, and Hilt for DI, and I added all rules and kept all models
This is the error:
Could not compute caller for function: public constructor MovieListEntity(movie_data: kotlin.collections.List<com...domain.entities.MovieData>) defined in com...domain.entities.MovieListEntity[c#dad1eb0] (member = null)
And these are the classes mentioned
data class MovieListEntity(
#field:Json(name = "movie_data")
val movie_data: List<MovieData>
)
data class MovieData(
#field:Json(name = "movie_id")
val movie_id: Int,
#field:Json(name = "sub_title")
val sub_title: String,
#field:Json(name = "title")
val title: String
)
Note: I tried also without annotations, and it didn't help
These are the proguard rules:
-keep class com.***.***.domain.entitie.** { *; }
-keep class com.***.***.domain.entities.*
-keep class com.***.***.domain.entities.MovieListEntity
-keep class com.***.***.domain.entities.MovieData
-keep class com.***.***.DataBinderMapperImpl { *; }
-keep class com.***.***.DataBinderMapperImpl { *; }
-keep class com.***.*****{
public ** component1();
<fields>;
}
Plus other rules for retrofit, OkHttp, hilt .. etc.
How Can I solve this error?
Solved by adding these rules
-keepclassmembers class kotlin.Metadata {
public <methods>;
}
-keepclassmembers class * {
#com.squareup.moshi.FromJson <methods>;
#com.squareup.moshi.ToJson <methods>;
}
-keepnames #kotlin.Metadata class com.******.domain.entities.**
-keep class com.******.domain.entities.** { *; }
-keepclassmembers class com.*****.domain.entities.** { *; }
The problem was in Moshi library, check this
Retrofit and moshi-kotlin Api's are not working in android.
I'm using dagger2 ,while genrating signed apk ,retrofit api calls are not working, in debug mode its working fine .
while we enabled minifyenable true and shrinkresource true.
#JsonClass(generateAdapter = true) #SuppressLint("ParcelCreator") #Keep data class DashboardListRes( #Json(name = "data") vardata: List<Data>?=ArrayList(), #Json(name = "lead_data") var leadData: LeadData= LeadData(), #Json(name = "message") var message: String="", #Json(name = "status") var status: String="", #Json(name = "status_code") var statusCode: Int=0, #Json(name = "user") var user: User=User() ):Serializable
1.
2. -keepattributes Signature, InnerClasses, EnclosingMethod
-keepattributes RuntimeVisibleAnnotations,
RuntimeVisibleParameterAnnotations -keepattributes
AnnotationDefault
-keepclassmembers,allowshrinking,allowobfuscation interface * {
#retrofit2.http.* <methods>; } -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement -dontwarn
javax.annotation.** -dontwarn kotlin.Unit -dontwarn
retrofit2.KotlinExtensions -dontwarn retrofit2.KotlinExtensions$*
-if interface * { #retrofit2.http.* <methods>; } -keep,allowobfuscation interface <1> -keep,allowobfuscation,allowshrinking interface retrofit2.Call -keep,allowobfuscation,allowshrinking class retrofit2.Response -keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation -dontwarn javax.annotation.**
-keepclasseswithmembers class * {
#com.squareup.moshi.* <methods>; }
-keep #com.squareup.moshi.JsonQualifier #interface *
-keepclassmembers #com.squareup.moshi.JsonClass class * extends java.lang.Enum {
<fields>;
**[] values(); } -keepclassmembers class com.squareup.moshi.internal.Util {
private static java.lang.String getKotlinMetadataClassName(); } -keepclassmembers class * {
#com.squareup.moshi.FromJson
<methods>; #com.squareup.moshi.ToJson <methods>; }
-keep class kotlin.Metadata
-keep class com.package.models.**{*;}
-keepclassmembers class com.package.dataclasses.** { *; }
-keepattributes Signature -keepattributes Annotation -dontwarn sun.misc.** -keep class com.google.gson.stream.** { *; } -keep class com.google.gson.examples.android.model.** {
<fields>; } -keep class com.credright.nikhil.models.** { *; }
-keep class com.package.ui.** { *; } -keep class * extends com.google.gson.TypeAdapter -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>; }
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken -dontwarn javax.annotation.**
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase -dontwarn
org.codehaus.mojo.animal_sniffer.* -dontwarn
okhttp3.internal.platform.ConscryptPlatform
-keep class org.apache.commons.logging.**
-keepattributes Annotation
-dontwarn org.apache.**
-keepnames #dagger.hilt.android.lifecycle.HiltViewModel class *
extends androidx.lifecycle.ViewModel
If you are converting the data received into a POJO object using some built in function and then populating a data class or something, you need to make sure you exclude the data classes from proguard.
What I generally do is I put all my data classes in one package and then exclude everything in that package like so:
-keepclassmembers class com.credright.nikhil.dataclasses.** { *; }
The line above should be added to the file proguard-rules.pro and dataclasses is the name of the package that has all the data classes in it.
The reason why this is important is that when proguard obfuscates your code, it basically changes all class names and everything into arbitrary things and so after obfuscation data classes cannot be populated.
There have historically been some difficulties when using moshi-kotli. Updating to a newer Kotlin version + updating some proguard rules fixes it.
You can check out this
GitHub comment.
I use Moshi for JSON conversions.
I have a custom adapter for converting LocalDateTime to JSON:
class LocalDateTimeAdapter {
#ToJson
fun toJson(value: LocalDateTime): String {
return value.toString()
}
#FromJson
fun fromJson(value: String): LocalDateTime {
return LocalDateTime.parse(value)
}
}
I use it like so:
val moshi = Moshi.Builder()
.addLast(KotlinJsonAdapterFactory())
.add(LocalDateTimeAdapter())
.build()
val jsonAdapter = moshi.adapter(MyModel::class.java)
val json = jsonAdapter.toJson(model)
This works fine for the debug build variant. The application crashes for other build variants where debugging is disabled with the following exception.
kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Could not compute caller for function: public constructor
I've put in hours trying to resolve this but to no avail. As far as I understand this is because of proguard. I've tried putting the following in the proguard file:
-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 *;
#com.squareup.moshi.ToJson *;
}
-keepclassmembers class com.squareup.moshi.internal.Util {
private static java.lang.String getKotlinMetadataClassName();
}
-keepnames #kotlin.Metadata class com.myapp.domain.model.**
-keep class com.myapp.domain.model.** { *; }
-keepclassmembers class com.myapp.domain.model.** { *; }
My application works well when proguard is disabled, but once enable it breaks the functionality.
I have a class called CachecManager which is not accessible once proguard is enabled.
Here is my class
object CacheManger {
fun setData(data: String) {
val yourFilePath: String = BaseClass.appContext.filesDir.toString() + "/" + USER_DATA
val yourFile = File(yourFilePath)
val fileOutputStream = FileOutputStream(yourFile)
fileOutputStream.write(data.toByteArray())
fileOutputStream.flush()
fileOutputStream.close()
}
}
This is my proguard file
##---------------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 TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in #JsonAdapter)
-keep class * extends com.google.gson.TypeAdapter
-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 ----------
#project related
-keep class com.appname.constants
-keep class com.appname.models.** { *; }
-keepnames class androidx.navigation.fragment.NavHostFragment
#-keep class <class>$Companion { *; }
-keep class kotlin.** { *; }
-keep class kotlin.Metadata { *; }
-dontwarn kotlin.**
-keepclassmembers class **$WhenMappings {
<fields>;
}
-keepclassmembers class kotlin.Metadata {
public <methods>;
}
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
}
This is error which i am getting
java.lang.ClassNotFoundException: Didn't find class "com.appname.cachemanager.CacheManger" on path: DexPathList[[zip file "/data/app/com.appname-2/base.apk"],nativeLibraryDirectories=[/data/app/com.appname-2/lib/arm64, /data/app/com.appname-2/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]
Current situation
I have a model, where class UserSettingModel have two field : long UserId and have as field one exemplar of class UserSettings ( with many field ) with name Settings.
WARNING : none of this fields used directly in my code ( in Android Studio color of this fields is gray), but I need to resend it to server.
public class UserSettingsModel
{
#SerializedName("UserId")
public long UserId = -1L;
#SerializedName("Settings")
public UserSettings Settings;
}//end of class/ UserSettingsModel
class UserSettings
{
#SerializedName("showRegion")
public String showRegion = "";
#SerializedName("showAddress")
public String showAddress = "";
}
Problem
If working with apk in DEBUG mode : GSON deserialize all field of class UserSettingModel, including Settings
If working with apk in RELEASE mode : field Settings - not deserialize
My proguard :
-keepattributes Signature
-keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
-keep class com.google.gson.examples.android.model.** { *; }
-keep class com.google.gson.** { *; }
-keepclassmembers enum * { *; }
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
What I need
How to serialize/deserialize with GSON in Release mode ALL FIELD of classes, also with warning "field is never used" ?
This is because of obfuscation, as DQuade states in the comment.
To prevent gradle from removing them, you can either add the specific class in your proguard-rules.pro file:
-keepclassmembers class [yourPackageName].UserSettingsModel
-keepclassmembers class [yourPackageName].UserSettings
or prevent gradle from removing all members annotated with the SerializedName annotation:
-keepclassmembers class * {
#com.google.gson.annotations.SerializedName <fields>;
}