In my project, I am performing API call using RxJava.
Without proguard, it is running fine. But when I apply proguard It gives java.lang.AssertionError: java.lang.NoSuchFieldException: HTTP_1_0
in onError(e: Throwable) of a subscriber.
I applied -keepclassmembers enum * { *; } in my proguard to prevent obfuscation.
Api Call
fun latestPosts(): Subscription {
return service.latestPosts
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(APICallSubscriber(presenterContract, ApiIndex.POSTS))
}
Subscriber
class APICallSubscriber<T>(private val callback: BasePresenterContract,
private val apiIndex: String) : Subscriber<Response<T>>() {
override fun onCompleted() {
}
override fun onError(e: Throwable) {
Log.d("HomeTest", "${e}")
}
override fun onNext(response: Response<T>) {
val jsonObject = App.gson().toJsonTree(response).asJsonObject
val responseCode = jsonObject
.get(PayloadKeys.RAW_RESPONSE).asJsonObject
.get(PayloadKeys.CODE).asInt
Log.d("HomeTest", "$jsonObject")
val body: JsonElement? = jsonObject.get(PayloadKeys.BODY)
if (body != null) {
val responseBody = body.asJsonObject
callback.onNetworkRequestCompletedWith(responseBody, responseCode, apiIndex)
} else {
val errorBody: JsonElement? = jsonObject.get(PayloadKeys.ERROR_BODY)
callback.onNetwordRequestError(errorBody!!.asJsonObject, apiIndex)
}
}
}
I have tried different proguard rules but no result.
Please help.
In case this helps anyone, I had an existing project with a library module I had made. To prevent my app from crashing after being minified by ProGuard, I needed to add
-keepclassmembers enum * { *; }
to not only the library's build.gradle, but also to the main module's build.gradle.
I was facing the same issue, I solved by adding all these in proguard-rules.pro file:
-keepclassmembers enum * { *; }
-keep class com.google.code.gson.* { *; }
-keepattributes *Annotation*, Signature, Exception
-keepclassmembers,allowobfuscation class * {
#com.google.gson.annotations.SerializedName <fields>;
}
below configuration always worked for me
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
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
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.** { *; }
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
The type Nothing in the Result.Error is erased while obfuscating by R8. Try to add in build.gradle file the following:
buildscript {
repositories {
// other repos are omitted
maven { url 'https://storage.googleapis.com/r8-releases/raw' }
}
dependencies {
classpath 'com.android.tools:r8:2.1.68'
// other dependencies are omitted
In proguard file declare the Result type to keep from obfuscation
-keep class [path_to_class].Result { *; }
-keep class [path_to_class].Result$** { *; }
In my current Kotlin Android project I just had to face the following problem:
For this class hierarchy
abstract class MyClass {
...
}
class MySubClassA: MyClass() {
...
}
class MySubClassB: MyClass() {
...
}
class MySubClassC: MyClass() {
...
}
I wrote a creator function like this:
private fun <T : MyClass> createMyClass(myClassType: KClass<T>): T? {
val constructor = myClassType.primaryConstructor
return constructor?.call()?.apply {
...
}
}
This worked nicely for the debug version but for the release version with R8 obfuscation turned on the constructor was alway null. My settings for R8 were this:
-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);
}
-keep class kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoaderImpl
After some reverse engineering of the Kotlin reflection API I came up with the following workaround:
private fun <T : MyClass> createMyClass(myClassType: KClass<T>): T? {
val constructor = myClassType.constructors.firstOrNull() {
it.valueParameters.isEmpty()
}
return constructor?.call()?.apply {
...
}
}
This seems to indicate, that the constructor information of the class is preserved by R8 but the meta information about a constructor being primary gets lost.
Does anybody know how to solve this in general?
I came across this issue and was able to resolve it using the #Keep annotation.
abstract class MyClass {
}
class MySubClassA #Keep constructor(): MyClass() {
}
class MySubClassB #Keep constructor(): MyClass() {
}
class MySubClassC #Keep constructor(): MyClass() {
}
I have the following ViewModel:
class SignInViewModel #Inject constructor(val api: BillingApi) : ViewModel() {
val googleApiClient: MutableLiveData<GoogleApiClient> = MutableLiveData()
}
On my Activity.onCreate(onSavedInstanceState: Bundle?) I have:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
signInViewModel = ViewModelProviders.of(this)
.get(SignInViewModel::class.java)
signInViewModel.googleApiClient.observe(this, Observer<GoogleApiClient?> {
... // here never gets trigged
}
Later on my code I have signInViewModel.googleApiClient.value = it. At this point (which happens after a button click, so I am in a resumed state) the I expected the LiveData to trigger my observer, but it does not.
While debugging I've noticed that my MutableLiveData is never on an active state.
What am I doing wrong? Please, I know I am using a GoogleApiClient instance in the example and that it should be initialized with the Activity with automanage and whatnot, but this is not the issue here. I want to set it dynamically and have my observer be triggered.
Edit: adding the code that calls the setValue
signInViewModel.someMethod(this)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Consumer {
// This gets called but the observe callback does **not**
signInViewModel.googleApiClient.value = it
}, errorCallback)
It turns out minifyEnabled was true. I haven't seen anything about proguard rules for architecture components.
Found this issue which is not resolved yet but has the configuration needed to make it pass:
-keepclassmembers class * implements android.arch.lifecycle.LifecycleObserver {
<init>(...);
}
-keepclassmembers class * extends android.arch.lifecycle.ViewModel {
<init>(...);
}
-keepclassmembers class android.arch.lifecycle.Lifecycle$State { *; }
-keepclassmembers class android.arch.lifecycle.Lifecycle$Event { *; }
-keepclassmembers class * {
#android.arch.lifecycle.OnLifecycleEvent *;
}
-keep class * implements android.arch.lifecycle.LifecycleObserver {
<init>(...);
}