Currently I have a library that is to be consumed by multiple other projects, however I have a problem with the code obfuscation when consuming the .aar in particular with some classes:
This one to handle backend responses or asynchronous tasks, etc.
sealed class Result<out T : Any> {
open class Success<out T : Any>(val data: T) : Result<T>()
open class Error(val error: ErrorModel) : Result<Nothing>()
}
And this one to act as a initializer or something like it to the library:
class LibApp private constructor(
val appContext: Context
) {
companion object {
@JvmStatic lateinit var instance: LibApp
private set
fun init(
appContext: Context
) {
if (this::instance.isInitialized.not()) {
instance = LibApp(appContext)
}
}
}
}
The rules I'm using are:
#noinspection ShrinkerUnresolvedReference
-keep class com.cross.project.compilation.testlib.response.** { *; }
-keep class com.cross.project.compilation.testlib.LibApp.** { *; }
Currently I have 3 build types and 2 flavors:
buildTypes {
release {
debuggable false
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
releaseDebug{
debuggable true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug{
debuggable true
minifyEnabled false
}
}
flavorDimensions "libFlavors"
productFlavors {
dev {
}
prod {
}
}
When I check the decompiled sources for release variants in the consumer project I get:
Result gets the < T > stripped from the Success return:
public sealed class Result<out T : kotlin.Any> private constructor() {
public open class Error public constructor(error: com.cross.project.compilation.testlib.response.ErrorModel) : com.cross.project.compilation.testlib.response.Result {
public final val error: com.cross.project.compilation.testlib.response.ErrorModel /* compiled code */
}
public open class Success<out T : kotlin.Any> public constructor(data: T) : com.cross.project.compilation.testlib.response.Result {
public final val data: T /* compiled code */
}
}
This changes the way something is coded within the variants, from something like this in dev:
return when (val response = accountService.getAccountData()) {
is Result.Success -> ConsumerResult.Success(response.data)
is Result.ErrorModel -> ConsumerResult.Failure(response.errorModel)
}
To this:
return when (val response = accountService.getAccountData()) {
is Result.Success<*> -> ConsumerResult.Success((response.data as AccountData))
is Result.ErrorModel -> ConsumerResult.Failure(response.errorModel)
}
The LibApp class gets removed for some reason.
I've tried to modify the rules to avoid these issues, but only succeding in keeping the LibApp class by applying the following:
-keepclasseswithmembers class com.cross.project.compilation.testlib.LibApp {
public *;
}
-keep @interface kotlin.Metadata {
*;
}
-keepattributes RuntimeVisibleAnnotations
And also add the @Keep annotation to every level in the class like:
@Keep class LibApp private constructor(
...
@Keep
companion object {
...
@Keep
fun init(
...
However I've had no luck modifying the rule to keep the generic return of the Result.Success, Any idea of what I'm doing wrong?
As an additional information, I'm using and building with the maven-publish plugin:
Android Gradle Plugin 4.0.1 Gradle Wrapper 6.1.1
This problem seems to have appeared after I upgraded from:
Android Gradle Plugin 3.6.3 Gradle Wrapper 5.6.4