android kotlin multiplatform kotlinx.serialization works on debug but does not work on release version
Asked Answered
R

1

6

We are making our next project in the company with kotlin multiplatform. Everything worked fine until I tried to create a release version for android to beta test. I got this error in release versions while everything works fine in debug.

[NetworkManager] unknown exception: l.a.w: Can't locate argument-less serializer for class a. For generic classes, such as lists, please provide serializer explicitly.

here this is project level build.gradle

buildscript {
ext.kotlin_version = '1.3.70'
ext.versions = [
        "ktor" : "1.3.2",
        "serialization" : "0.20.0",
        "coroutines" : "1.3.6",
]

repositories {
    maven { url 'https://maven.google.com' }
    maven { url 'https://dl.google.com/dl/android/maven2' }
    maven { url "https://s3-eu-west-1.amazonaws.com/elasticode-sdk/Android/maven/" }
    google()
    jcenter()

}
dependencies {
    classpath 'com.android.tools.build:gradle:3.6.3'
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
    classpath 'com.google.firebase:firebase-crashlytics-gradle:2.2.0'
    classpath 'com.google.gms:google-services:4.3.3'
    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
}
}

plugins {
    id 'org.jetbrains.kotlin.multiplatform' version '1.3.70' // or any other kotlin plugin
    id 'org.jetbrains.kotlin.plugin.serialization' version '1.3.70'
}

allprojects {
    repositories {
        maven { url 'https://maven.google.com' }
        maven { url "https://s3-eu-west-1.amazonaws.com/elasticode-sdk/Android/maven/" }
        google()
        jcenter()

    }
}

and this is common modules build.gradle

apply plugin: 'kotlin-multiplatform'
apply plugin: 'kotlinx-serialization'

kotlin {
targets {
    iosArm64("ios64")
    iosX64("iosSim")

    configure([
            ios64,
            iosSim
    ]) {
        binaries.framework {
            baseName = 'PassengerCommon'
        }
    }

    fromPreset(presets.jvm, 'android')
}

sourceSets {
    commonMain.dependencies {
        implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
        implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

        implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:${versions.serialization}"

        implementation "io.ktor:ktor-client:${versions.ktor}"
        implementation "io.ktor:ktor-client-json:${versions.ktor}"
        implementation "io.ktor:ktor-client-serialization:${versions.ktor}"
        implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:${versions.coroutines}"
    }

    iosMain {
        dependsOn commonMain
        iosSimMain.dependsOn(it)
        ios64Main.dependsOn(it)
        dependencies {
            // HTTP
            implementation "io.ktor:ktor-client-ios:${versions.ktor}"
            implementation "io.ktor:ktor-client-json-native:${versions.ktor}"
            implementation "io.ktor:ktor-client-serialization-native:${versions.ktor}"

            // Coroutines
            api "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:${versions.coroutines}"
        }
    }

    iosSimMain {
        dependencies {
            implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-iosx64:${versions.serialization}"
        }
    }

    ios64Main {
        dependencies {
            implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-iosarm64:${versions.serialization}"
        }
    }

    androidMain.dependencies {
        implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
        implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

        implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:${versions.serialization}"

        implementation "io.ktor:ktor-client-serialization-jvm:${versions.ktor}"
        implementation "io.ktor:ktor-client-okhttp:${versions.ktor}"
        implementation "com.squareup.okhttp3:logging-interceptor:4.0.1"

        implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutines}"
    }
}

// Create a task building a fat framework.
task debugFatFramework(type: FatFrameworkTask) {
    // The fat framework must have the same base name as the initial frameworks.
    baseName = 'PassengerCommon'

    // The default destination directory is '<build directory>/fat-framework'.
    destinationDir = file("$buildDir/fat-framework/debug")

    // Specify the frameworks to be merged.
    from(
            targets.ios64.binaries.getFramework("DEBUG"),
            targets.iosSim.binaries.getFramework("DEBUG")
    )
}

// Create a task building a fat framework.
task releaseFatFramework(type: FatFrameworkTask) {
    // The fat framework must have the same base name as the initial frameworks.
    baseName = 'PassengerCommon'

    // The default destination directory is '<build directory>/fat-framework'.
    destinationDir = file("$buildDir/fat-framework/release")

    // Specify the frameworks to be merged.
    from(
            targets.ios64.binaries.getFramework("RELEASE"),
            targets.iosSim.binaries.getFramework("RELEASE")
    )
}
}


configurations {
    compileClasspath
}

lastly this is the android modules build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.google.gms.google-services'

repositories {
    flatDir {
        dirs 'libs'
    }
    google()
    mavenCentral()
}

android {
compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}
compileSdkVersion 28
defaultConfig {
    applicationId "com.lines.passenger"
    minSdkVersion 21
    targetSdkVersion 28
    versionCode 53
    versionName "2.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    packagingOptions {
        exclude 'META-INF/proguard/androidx-annotations.pro'
    }
}
signingConfigs {
    release {
        try {
            storeFile file('../keystores/release.jks')
            storePassword *****
            keyAlias "*****"
            keyPassword *****
        } catch (Exception ex) {
            throw new Exception("You should define KEYSTORE_PASSWORD and KEY_PASSWORD in gradle.properties.")
        }
    }
    debug {
        storeFile file('../keystores/debug.keystore')
    }
}
lintOptions {
    checkReleaseBuilds false
    abortOnError false
}
buildTypes {
    debug {
        applicationIdSuffix '.debug'
        versionNameSuffix '-DEBUG'
        resValue("string", "app_name", "Passenger")
        signingConfig signingConfigs.debug
        resValue("string", "g_maps_api_key", "*****")
        manifestPlaceholders = [MAPS_API_KEY: "*****"]

        resValue "string", "app_version", "${defaultConfig.versionName}"
        resValue("string", "PUSHER_APP_KEY", "*****")
        resValue("string", "new_relic_key", "*****")
    }
    release {
        resValue "string", "app_version", "${defaultConfig.versionName}"
        minifyEnabled true
        shrinkResources true
        zipAlignEnabled true
        resValue("string", "app_name", "Lines")
        signingConfig signingConfigs.release
        resValue("string", "g_maps_api_key", "*****")
        manifestPlaceholders = [MAPS_API_KEY: "*****"]
        resValue("string", "PUSHER_APP_KEY", "*****")
        resValue("string", "new_relic_key", "*****")

        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}
packagingOptions {
    exclude 'META-INF/*.kotlin_module'
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
    exclude group: 'com.android.support', module: 'support-annotations'
})
testImplementation 'junit:junit:4.12'
testImplementation 'org.hamcrest:hamcrest-library:1.3'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
testImplementation "io.mockk:mockk:1.9"
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2'

implementation project(':PassengerCommon')


annotationProcessor 'com.jakewharton:butterknife-compiler:10.0.0'

implementation 'com.google.android.gms:play-services-location:17.0.0'
implementation 'com.google.android.gms:play-services-maps:17.0.0'
implementation 'com.google.firebase:firebase-messaging:20.2.1'
implementation 'com.google.firebase:firebase-core:17.4.3'
implementation 'com.google.firebase:firebase-config:19.1.4'
implementation 'com.google.firebase:firebase-crashlytics:17.1.0'
implementation 'com.google.firebase:firebase-storage:19.1.1'
implementation 'com.google.android.gms:play-services-analytics:17.0.0'
implementation 'com.android.support:multidex:1.0.3'
implementation 'com.google.maps.android:android-maps-utils:0.4.4'

implementation 'com.afollestad.material-dialogs:core:0.9.5.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.2'
implementation 'com.squareup.retrofit2:retrofit:2.2.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
implementation 'com.squareup.retrofit2:converter-gson:2.2.0'
implementation 'com.jakewharton:butterknife:10.0.0'
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.5.0")
implementation 'com.karumi:dexter:4.2.0'
implementation 'de.hdodenhof:circleimageview:2.1.0'
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.prolificinteractive:material-calendarview:1.4.3'
implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'
implementation 'q.rorbin:badgeview:1.1.3'
implementation 'com.jakewharton.rxbinding3:rxbinding:3.0.0-alpha2'
implementation 'info.hoang8f:android-segmented:1.0.6'
implementation 'com.elasticode.sdk:elasticode-android:2.1.1'
implementation 'net.cachapa.expandablelayout:expandablelayout:2.9.2'

implementation 'com.pusher:pusher-websocket-android:0.7.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutines}"
}
apply plugin: 'com.google.gms.google-services'

If you guys need anything I will gladly provide

Renick answered 29/6, 2020 at 12:31 Comment(0)
V
12

If the app works in debug but not in release, it's probably because Proguard minified your classes and deserialization no longer works due to different (shorter) class and property names.

Make sure you have this in your proguard:

-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.SerializationKt
-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(...);
}

If deserialization still fails, please provide an example of how you are deserializing network response(s) into data classes.

Vermiculite answered 1/7, 2020 at 11:8 Comment(1)
Great job! Shortest solution I found concerning this messy topic.Chockfull

© 2022 - 2024 — McMap. All rights reserved.