Serializer for data class Kotlin not found at build release - android

I want convert my json string response from API to object:
val obj = Json.decodeFromString<MyModel>(jsonResponseString)
My data class:
#Serializable
data class MyModel(
#SerializedName("field") val field: String
)
It look like very simple and it works on debug mode!
But when a compiled the AppBundle, builded in release mode and download app from Play Store internal testing, I got the following error :
Serializer for class '...' is not found. Mark the class as #serializable or provide the
serializer explicitly.
kotlinx.serialization.internal.Platform_commonKt.serializerNotRegistered

You should add this to your proguard.pro if you're using minifyEnabled true
-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations
# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
-keepclassmembers class kotlinx.serialization.json.** {
*** Companion;
}
-keepclasseswithmembers class kotlinx.serialization.json.** {
kotlinx.serialization.KSerializer serializer(...);
}
# Change here com.yourcompany.yourpackage
-keep,includedescriptorclasses class com.yourcompany.yourpackage.**$$serializer { *; } # <-- change package name to your app's
-keepclassmembers class com.yourcompany.yourpackage.** { # <-- change package name to your app's
*** Companion;
}
-keepclasseswithmembers class com.yourcompany.yourpackage.** { # <-- change package name to your app's
kotlinx.serialization.KSerializer serializer(...);
}
Make sure you replace the placeholder package name with your app package name
Source

I found the next solution:
First step, I added #Keep anotation. Keep anotation denotes that the annotated element should not be removed when the code is minified at build time:
#Keep
#Serializable
data class MyModel(
#SerializedName("field") val field: String
)
Second step, I converted my json to object making a static reference to the serializer:
val objError = Json {ignoreUnknownKeys = true}.decodeFromString(MyModel.serializer(), jsonResponseString)
Dont forget import and implement last version of:
'org.jetbrains.kotlin.plugin.serialization'
And it worked and it save my day!!

I have fixed that with these changes in the Gradle files, in the build project gradle add this line to dependencies:
classpath "org.jetbrains.kotlin:kotlin-serialization:1.5.21"
Add also these to the build app gradle:
plugins {
...
id 'kotlinx-serialization'
}
dependencies {
...
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2'
}
Please note version numbers might be different.

Starting from 1.5.0-RC proguard rules are shipped with library.
Bundled Proguard rules
The kotlinx-serialization-core-jvm JAR file now includes consumer Proguard >rules,
so manual Proguard configuration is no longer necessary for most of the setups.

Related

Kotlin 1.6.0 (There are no problems in 1.5.21) + Proguard + Gson (Registering an InstanceCreator with Gson...)

I have enabled Proguard and configured the rules
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
For GSON I used these rules
added my models to exceptions (-keep class my.package.model.** { *; })
Also used other rules for various libraries
I have this error -> Registering an InstanceCreator with Gson for this type may fix this problem.
the error occurred when switching to kotlin 1.6.0 and 1.6.10.
There is no error on version Kotlin 1.5.21!
Error (I have a bug only on Kotlin 1.6.0/1.6.10. There is no error on kotlin 1.5.21) --->
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.project.main, PID: 2943
java.lang.RuntimeException: Unable to invoke no-args constructor for class com.project.main.domain.entity.MyEntityClass. Registering an InstanceCreator with Gson for this type may fix this problem.
at com.google.gson.internal.c$e.a(SourceFile:228)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(SourceFile:212)
at com.google.gson.Gson.h(SourceFile:963)
at com.google.gson.Gson.j(SourceFile:928)
at com.google.gson.Gson.l(SourceFile:877)
at com.project.main.data.cache.converter.MyEntityClassConverter.toMyEntityClass(SourceFile:14)
at com.project.main.data.cache.dao.MyProjectDao_Impl$21.call(SourceFile:947)
at com.project.main.data.cache.dao.MyProjectDao_Impl$21.call(SourceFile:889)
at k1.c$a$a$a$a.invokeSuspend(SourceFile:128)
at uh.a.resumeWith(SourceFile:33)
at uk.z0.run(SourceFile:106)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
Caused by: java.lang.UnsupportedOperationException: Abstract class can't be instantiated! Class name: com.project.main.domain.entity.MyEntityClass
at com.google.gson.internal.l.a(SourceFile:120)
at com.google.gson.internal.l$a.c(SourceFile:49)
at com.google.gson.internal.c$e.a(SourceFile:225)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(SourceFile:212) 
at com.google.gson.Gson.h(SourceFile:963) 
at com.google.gson.Gson.j(SourceFile:928) 
at com.google.gson.Gson.l(SourceFile:877) 
at com.project.main.data.cache.converter.MyEntityClassConverter.toMyEntityClass(SourceFile:14) 
at com.project.main.data.cache.dao.MyProjectDao_Impl$21.call(SourceFile:947) 
at com.project.main.data.cache.dao.MyProjectDao_Impl$21.call(SourceFile:889) 
at k1.c$a$a$a$a.invokeSuspend(SourceFile:128) 
at uh.a.resumeWith(SourceFile:33) 
at uk.z0.run(SourceFile:106) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
at java.lang.Thread.run(Thread.java:923) 
I have rules for this error.
-keep class com.project.main.domain.entity.MyEntityClass { ; }
but I'm adding the whole package
-keep class com.project.main.domain.entity.* { *; }
I also have all the other rules for GSON -> rules
#TypeConverter
fun toMyEntityClass(value: String): MyEntityClass =
gson.fromJson(value, object : TypeToken<MyEntityClass>() {}.type) //Error
I also tried to prescribe just one rule. The rule helps to run the project on 1.5.21 and works. But it doesn't work on 1.6.0 - 1.6.10. I don't want to create an Instance Creator. I have too many models and I will waste a lot of time.
-keep class ** { *; }
For any classes that interact with GSON you have to keep as follow :
-keep class "MODEL CLASSES"
for your issue you can do like :
-keep class com.project.main.domain.entity.** { *; }
It will keep all classes under the entity package.
The problem was solved by itself, after updating the libraries
gson 2.9.0
and kotlin 2.6.21
I didn't do anything, just updated to these versions
I haven't checked 1.6.0 and 1.6.10 works in tandem with gson 2.9.0 but gson didn't work with the previous release version
As a result, I upgraded Kotlin from 1.5.21 -> 1.6.21

Cannot serialize Kotlin type with Moshi AND R8

i have this strange problem. When using minify/obfuscation with R8 i have the following error
java.lang.IllegalArgumentException: Cannot serialize Kotlin type <omitted>auth.model.ErrorResponse. Reflective serialization of Kotlin classes without using kotlin-reflect has undefined and unexpected behavior. Please use KotlinJsonAdapter from the moshi-kotlin artifact or use code gen from the moshi-kotlin-codegen artifact.
With no minify/obfuscation everything it's ok. I'm using
val moshi = Moshi.Builder()
.add(BigDecimalAdapter)
.add(KotlinJsonAdapterFactory())
.build()
as readme from moshi, stated. Also in gradle i'm using
implementation("com.squareup.moshi:moshi-kotlin:1.9.3")
and added rules
-keep class com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
-keep class <omitted>auth.model.ErrorResponse
but still have the same exception. what am i missing ?
Thanks
updated
here's my BigDecimalAdapter
object BigDecimalAdapter {
#FromJson
fun fromJson(string: String) = BigDecimal(string)
#ToJson
fun toJson(value: BigDecimal) = value.toString()
}
try adding to your proguard:
-keepclassmembers class * {
#com.squareup.moshi.FromJson <methods>;
#com.squareup.moshi.ToJson <methods>;
}

Android uses the "default" method in the interface to generate apk

Now there is a third-party dependency package: page
It has an interface that contains the default method :
public interface TempInterface {
default String getStr() {
return "test";
}
}
And i create a module : app
There is a class in "app" that uses the above interface
public class AA implements TempInterface {
}
"build.gradle" file in "app" :
dependencies {
compileOnly project(':page')
}
In the generated APK, the following classes appear:
what about *$-CC ?????
And when I decompiled the DEX file, I found that there was no such class at all:
So I'm confused about what this "* $- CC" is ??
Who can help me
the build.gradle :
$-CC file is generated by d8 when the class file compile to dex file.
More details on https://mouaad.aallam.com/java-8-interface-methods-for-android/
why you apply proguard from your app/build.gradle, it obfuscates your class and methods name so that reverse engineering is tough. that is what happens here

Realm Kotlin not in schema

I try to use Realm.io to store data in android i have RealmClass
#RealmClass
public open class Alarm : RealmObject() {
#Required
public open var hourOfDay: Int? = null
#Required
public open var minute: Int? = null
#Required
public open var days: BooleanArray? = null
public open var name: String? = null
}
and in onCreate i try to add some test data
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mRealm = Realm.getInstance(activity);
mRealm.beginTransaction()
var a = mRealm.createObject(Alarm::class.java)
a.hourOfDay = 12
a.minute = 1;
a.days = booleanArrayOf(true, true, true, true, true, false, false);
a.name = "Test${System.currentTimeMillis()}"
mRealm.commitTransaction()
}
but i get exception.
> java.lang.IllegalArgumentException: Alarm is not part of the schema for this Realm
at var a = mRealm.createObject(Alarm::class.java)
i have set realm rules
-keep class io.realm.annotations.RealmModule
-keep #io.realm.annotations.RealmModule class *
-keep class io.realm.internal.Keep
-keep #io.realm.internal.Keep class *
-dontwarn javax.**
-dontwarn io.realm.**
in > proguard-rules.pro
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
productFlavors {
realmFlavor {
proguardFile 'proguard-rules.pro'
}
}
but this did not bring solution
CONCLUSION
so far its looks like we have to write models in Java, Realm and any ORM libraries Kotlin examples posted on GitHub have models written in pure Java
"java.lang.NoClassDefFoundError: io/realm/annotations/RealmClass"
If you run into the error above, You'll most likely have to add something like this to your build.gradle. The bottom two lines for kapt are key.
//Realm
compile 'io.realm:realm-android:0.87.1'
kapt "io.realm:realm-annotations:0.87.1"
kapt "io.realm:realm-annotations-processor:0.87.1"
Per the comment at the bottom of this issue: https://github.com/realm/realm-java/issues/509
For Kotlin to work with Realm's annotation processor you need the following setup in your build.gradle
compile "io.realm:realm-android-library:0.86.0#aar"
compile "io.realm:realm-annotations:0.86.0"
kapt "io.realm:realm-annotations-processor:0.86.0"
For me, I added #RealmClass to all models and fixed despite that it is not required.
I think it forces to run Realm processor correctly by using annotator.
To check RealmObject models are processed or not, your Gradle console output (you can open it in Android Studio at the right bottom toggle panel) has to show Red-colored model processing log as below.
:app:compileDebugJavaWithJavac
Full recompilation is required because at least one of the classes of removed jar 'kotlin-stdlib-1.0.0.jar' requires it. Analysis took 1.572 secs.
Note: Processing class ModelSomething
Note: Processing class ModelAnotherThing
Note: Creating DefaultRealmModule
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
:app:transformClassesWithRealmTransformerForDebug
:app:transformClassesWithInstantRunVerifierForDebug
:app:transformClassesWithJavaResourcesVerifierForDebug
The Note: lines are red colors and you can see Processing class ModelSomething message.
All of your model should be listed as this message.
This issue is filed as a suspicious bug: https://github.com/realm/realm-java/issues/2822

Obfuscating the .aar files

I have created the .aar file (containing the resources & drawables) of an Android library project using
./gradlew assemble
I have enabled obfuscating by setting minify == true
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
However when I run the mentioned gradle command with minify enabled = true, I get java.io.IOException: The output jar is empty. Did you specify the proper '-keep' options?
What does this error pointing to and how can I obfuscate the library .aar file?
Best Regards
Using Proguard worked like a charm for me!
Proguard is used to shrink, obfuscate, optimize code. Proguard is
necessary for your library to remove unused code and make reverse
engineering little difficult. Proguard rules for the library are
different from the normal applications. As you know, Proguard renames
classes, variables, and methods using meaningless names. You would
like to keep the names of those methods and classes as it is that
developers will call. You will need to test and verify obfuscated code
from generated AAR file.
Library Module
build.gradle of your library
buildTypes {
release {
// Enables code shrinking, obfuscation, and optimization for only
// your project's release build type.
minifyEnabled true
// Includes the default ProGuard rules files that are packaged with
// the Android Gradle plugin. To learn more, go to the section about
// R8 configuration files.
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
Inside of your proguard-rules.pro of your library
# Save the obfuscation mapping to a file, so we can de-obfuscate any stack
# traces later on. Keep a fixed source file attribute and all line number
# tables to get line numbers in the stack traces.
# You can comment this out if you're not interested in stack traces.
-printmapping out.map
-keepparameternames
-renamesourcefileattribute SourceFile
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,EnclosingMethod
# Preserve all annotations.
-keepattributes *Annotation*
# Preserve all public classes, and their public and protected fields and
# methods.
-keep public class * {
public protected *;
}
# Preserve all .class method names.
-keepclassmembernames class * {
java.lang.Class class$(java.lang.String);
java.lang.Class class$(java.lang.String, boolean);
}
# Preserve all native method names and the names of their classes.
-keepclasseswithmembernames class * {
native <methods>;
}
# Preserve the special static methods that are required in all enumeration
# classes.
-keepclassmembers class * extends java.lang.Enum {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# Explicitly preserve all serialization members. The Serializable interface
# is only a marker interface, so it wouldn't save them.
# You can comment this out if your library doesn't use serialization.
# If your code contains serializable classes that have to be backward
# compatible, please refer to the manual.
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# The library may contain more items that need to be preserved;
# typically classes that are dynamically created using Class.forName:
# -keep public class mypackage.MyClass
# -keep public interface mypackage.MyInterface
# -keep public class * implements mypackage.MyInterface
Thanks to .... reference
https://dev.to/mohitrajput987/develop--publish-your-own-sdk-in-android---part-2getting-started-with-sdk-development-3159
Proguard cuts unused classes. Libraries are standalone products, and has some specific entry points, which should not be obfsuscated. So you need to add rules to keep this entry points. Rules lookes like this:
-keep class packagename {public *;}
Copy library.pro file to your library project from this location:
...\android-sdk\tools\proguard\examples
Comment these lines when building from Android Studio, it should be probably kept/updated when building from the command line:
-injars in.jar
-outjars out.jar
-libraryjars /lib/rt.jar
Update your library project build.gradle file to use library.pro:
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'library.pro'
}
Sync and build the project and it should generate an obfuscated AAR file now.
Two suggestions:
Please ensure this file exist in the library module:
proguard-rules.pro.
Please try running "./gradlew :mylib:assembleRelease" with the
"mylib" be replaced with your library module name.

Categories

Resources