retrofit icon indicating copy to clipboard operation
retrofit copied to clipboard

After enable R8 full mode getting ParameterizedType error

Open NG-Gaurav opened this issue 2 years ago • 2 comments

Accessing hidden method Ljava/security/spec/ECParameterSpec;->setCurveName(Ljava/lang/String;)V (greylist, reflection, allowed) 16:09:29.013 W java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType 16:09:29.014 W at retrofit2.HttpServiceMethod.parseAnnotations(SourceFile:46) 16:09:29.014 W at retrofit2.ServiceMethod.parseAnnotations(SourceFile:39) 16:09:29.014 W at retrofit2.Retrofit.loadServiceMethod(SourceFile:202) 16:09:29.014 W at retrofit2.Retrofit$1.invoke(SourceFile:160) 16:09:29.014 W at java.lang.reflect.Proxy.invoke(Proxy.java:1006) 16:09:29.014 W at $Proxy6.generateAnonymousAuthTokenAsyn(Unknown Source)

NG-Gaurav avatar Jul 08 '22 06:07 NG-Gaurav

I'm facing a very similar issue. When i fire up the request with proguard enabled in R8 fullmode this exception raises:

java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType at retrofit2.HttpServiceMethod.parseAnnotations(SourceFile:46) at retrofit2.ServiceMethod.parseAnnotations(SourceFile:39) at retrofit2.Retrofit.loadServiceMethod(SourceFile:202) at retrofit2.Retrofit$1.invoke(SourceFile:160) at java.lang.reflect.Proxy.invoke(Proxy.java:1006)

I believe that probably is a bug with R8 fullmode enabled. I'm using retrofit 2.9.0 + okHttp3 4.2.0 + square-gson-converter 2.6.1 + kotlin coroutines.

marcoscostaanddev avatar Jul 21 '22 17:07 marcoscostaanddev

New proguard rules added but not published yet, you can add these rules to your proguard-rules files.

https://github.com/square/retrofit/blob/6cd6f7d8287f73909614cb7300fcde05f5719750/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro#L34-L41

See #3579 & #3598.


EDIT: Now you can just simplify use these

https://github.com/square/retrofit/blob/2f12836535128a3d0e8fe8a2ac0b46413e6a28a0/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro#L38-L48

Goooler avatar Jul 22 '22 00:07 Goooler

Now that Android Studio Flamingo with AGP 8.0 enabled R8 full mode by default, I'm facing the same issue.

Could you please release a new version of Retrofit with the updated consumer rules?

svenjacobs avatar Apr 14 '23 08:04 svenjacobs

You can just copy-paste the rules https://github.com/square/retrofit/issues/3751#issuecomment-1192043644 into your project.

Goooler avatar Apr 14 '23 08:04 Goooler

You can just copy-paste the rules #3751 (comment) into your project.

I know, but not having to maintain R8 rules for third-party libraries is much nicer 😉

svenjacobs avatar Apr 14 '23 08:04 svenjacobs

@Goooler It doesn't actually work, at least for me with AGP 8.0. After adding this proguard lines I get a slightly different error. Perhaps the reason is in the sandwich library, but I'm not sure:

 java.lang.IllegalArgumentException: Unable to create call adapter for retrofit2.Call<com.skydoves.sandwich.ApiResponse>
                     for method ComicVineService.issues
                 	at retrofit2.Utils.methodError(SourceFile:47)
                 	at retrofit2.HttpServiceMethod.parseAnnotations(SourceFile:394)
                 	at retrofit2.Retrofit.loadServiceMethod(SourceFile:31)
                 	at retrofit2.Retrofit$1.invoke(SourceFile:45)
                 	at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
                 	at $Proxy9.issues(Unknown Source)
                        ...
Caused by: java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
                 	at com.skydoves.sandwich.adapters.ApiResponseCallAdapterFactory.get(SourceFile:53)
                 	at retrofit2.Retrofit.callAdapter(SourceFile:33)
                 	at retrofit2.HttpServiceMethod.parseAnnotations(SourceFile:375)
                 	... 41 more
 @GET("issues?format=$FORMAT&field_list=${Fields.Issues}")
    @JvmSuppressWildcards
    suspend fun issues(
        @Query("api_key") apiKey: String,
        @Query("offset") offset: Int,
        @Query("limit") limit: Int,
        @Query("sort", encoded = true) sort: ComicVineSort?,
        @Query("filter[]", encoded = true) filter: List<ComicVineFilter>?,
    ): ApiResponse<IssuesResponse>

proninyaroslav avatar Apr 16 '23 19:04 proninyaroslav

Things about Sandwich will be fixed in https://github.com/skydoves/sandwich/pull/103.

Goooler avatar Apr 18 '23 08:04 Goooler

New proguard rules added but not published yet, you can add these rules to your proguard-rules files.

https://github.com/square/retrofit/blob/6cd6f7d8287f73909614cb7300fcde05f5719750/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro#L34-L41

See #3579 & #3598.

Hi I have added these rules to my project and still getting the same error, did this work for anyone?

java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:46) at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:39) at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:202) at retrofit2.Retrofit$1.invoke(Retrofit.java:160) at java.lang.reflect.Proxy.invoke(Proxy.java:1006)

jafar-alrashid avatar Apr 20 '23 07:04 jafar-alrashid

The R8 FAQ states the following:

Attributes (such as Signature) and annotations are only kept for classes, methods and fields which are matched by keep rules even when -keepattributes is specified. The weakest rule that will keep annotations and attributes is -keep[classmembers],allowshrinking,allowoptimization,allowobfuscation,allowaccessmodification class-specification Additionally, for attributes describing a relationship such as InnerClass and EnclosingMethod, non-compat mode requires both endpoints being kept.

Does anyone know why the rules in retrofit only contain keep,allowobfuscation,allowshrinking but missing allowoptimization and allowaccessmodification?

The ProGuard documentation states that allowaccessmodification should probably not be used for libraries:

Counter-indication: you probably shouldn't use this option when processing code that is to be used as a library, since classes and class members that weren't designed to be public in the API may become public.

But what about the allowoptimization?

G00fY2 avatar Apr 20 '23 18:04 G00fY2

Now that Android Studio Flamingo with AGP 8.0 enabled R8 full mode by default, I'm facing the same issue.

Could you please release a new version of Retrofit with the updated consumer rules?

Did you able to solve it?

chiragthummar avatar Apr 21 '23 10:04 chiragthummar

Did you able to solve it?

@chiragthummar I just copied these rules into my project but I would still prefer a new Retrofit release 🙏🏼

svenjacobs avatar Apr 21 '23 10:04 svenjacobs

Thanks you for your answer @svenjacobs

chiragthummar avatar Apr 21 '23 10:04 chiragthummar

Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items).

-keep,allowobfuscation,allowshrinking interface retrofit2.Call -keep,allowobfuscation,allowshrinking class retrofit2.Response

With R8 full mode generic signatures are stripped for classes that are not

kept. Suspend functions are wrapped in continuations where the type argument

is used.

-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation

Same, have you found the issue? This is my exact issue if it helps - https://stackoverflow.com/questions/76118721/r8-full-mode-throws-class-cast-exception-agp-8-0

DarkAbhi avatar Apr 27 '23 02:04 DarkAbhi

ChatGPT gave me this as the answer

The java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType error can occur with Retrofit and Moshi when using R8 full mode because R8 removes the generic type information during the code optimization process. Here's how you can fix this issue:

Add the following Proguard/R8 rule to your proguard-rules.pro file to keep the generic type information:

-keepattributes Signature

If you're using Moshi, add the following Proguard/R8 rule to your proguard-rules.pro file to keep the classes that are used for JSON serialization and deserialization:

-keepclassmembers class com.example.MyClass {
  @com.squareup.moshi.FromJson *;
  @com.squareup.moshi.ToJson *;
}

Replace com.example.MyClass with the name of your class.

If you're using Kotlin, add the following Proguard/R8 rule to your proguard-rules.pro file to keep the Kotlin metadata:

-keepclassmembers class com.example.MyClass {
  kotlin.Metadata <fields>;
}

Replace com.example.MyClass with the name of your class.

If you're still experiencing the issue, you can try using the TypeToken class from the Gson library instead of the ParameterizedType class. To use TypeToken, replace the following code:

Type type = new TypeToken<List<MyClass>>(){}.getType();

with:

Type type = Types.newParameterizedType(List.class, MyClass.class);

Note that you'll need to add the following dependency to your build.gradle file:

implementation 'com.google.code.gson:gson:2.8.9'

Finally, if you're using a custom Moshi JsonAdapter, you can try adding the @JsonClass(generateAdapter = true) annotation to your data class to generate the adapter at compile-time. This can help to avoid issues with R8. For example:

@JsonClass(generateAdapter = true)
data class MyClass(val name: String)

These steps should help you to resolve the java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType error when using Retrofit and Moshi with R8 full mode.

DarkAbhi avatar Apr 27 '23 02:04 DarkAbhi

If any of you are using a custom sealed class to represent your API calls state, make sure you include that, as done in the sandwich.pro file in this PR. Your final rules can look like:

# https://github.com/square/okhttp/blob/339732e3a1b78be5d792860109047f68a011b5eb/okhttp/src/jvmMain/resources/META-INF/proguard/okhttp3.pro#L11-L14
-dontwarn okhttp3.internal.platform.**
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
-dontwarn org.openjsse.**

# TODO: Waiting for new retrofit release to remove these rules
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
-keep,allowobfuscation,allowshrinking class retrofit2.Response
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation

-keep,allowobfuscation,allowshrinking class com.your.company.YourCustomSealedClass

eury-fsl avatar Apr 27 '23 14:04 eury-fsl

@eury-fsl will try this and get back.

DarkAbhi avatar Apr 27 '23 15:04 DarkAbhi

My Working Solutions

##---------------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>;
}


-keep class com.google.gson.reflect.TypeToken
-keep class * extends com.google.gson.reflect.TypeToken
-keep public class * implements java.lang.reflect.Type


# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken

##---------------End: proguard configuration for Gson  ----------

chiragthummar avatar Apr 27 '23 15:04 chiragthummar

If any of you are using a custom sealed class to represent your API calls state, make sure you include that, as done in the sandwich.pro file in this PR. Your final rules can look like:

# https://github.com/square/okhttp/blob/339732e3a1b78be5d792860109047f68a011b5eb/okhttp/src/jvmMain/resources/META-INF/proguard/okhttp3.pro#L11-L14
-dontwarn okhttp3.internal.platform.**
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
-dontwarn org.openjsse.**

# TODO: Waiting for new retrofit release to remove these rules
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
-keep,allowobfuscation,allowshrinking class retrofit2.Response
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation

-keep,allowobfuscation,allowshrinking class com.your.company.YourCustomSealedClass

Yup, when using https://github.com/arrow-kt/arrow-integrations/tree/main/arrow-integrations-retrofit-adapter arrow adapter which turns the response into and arrow.core.Either<>

I had to add exactly what was said above + -keep,allowobfuscation,allowshrinking class arrow.core.Either and it now works

StylianosGakis avatar Apr 27 '23 15:04 StylianosGakis

@eury-fsl will try this and get back.

Okay this doesn't work I'm convinced I've got something to deal with Moshi.

This is my issue if anyone has a clue, thanks

Github issue - https://github.com/square/retrofit/issues/3751#issuecomment-1515817159 Stackoverflow - https://stackoverflow.com/questions/76118721/r8-full-mode-throws-class-cast-exception-agp-8-0

DarkAbhi avatar Apr 27 '23 15:04 DarkAbhi

@DarkAbhi Can you show me your rules and code for one of your APIs (the interface).

eury-fsl avatar Apr 27 '23 16:04 eury-fsl

@eury-fsl here, this is my proguard

-keep class  com.iku.community_chat.data.room.entity.** { *; }
-keep class androidx.appcompat.widget.** { *; }
#Jsoup
-keep public class org.jsoup.** {
public *;
}
-keepnames class com.iku.user.profile.data.room.models.** { *; }
-keepnames class com.iku.community_courses.data.room.entity.** { *; }
-keeppackagenames org.jsoup.nodes

# Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items).
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
-keep,allowobfuscation,allowshrinking class retrofit2.Response

# With R8 full mode generic signatures are stripped for classes that are not
# kept. Suspend functions are wrapped in continuations where the type argument
# is used.
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
-keep,allowobfuscation,allowshrinking class com.squareup.moshi.JsonAdapter

My ApiService

interface IkuApiService {
/**
     * Check if email exists
     *
     * @param email
     * @param authorization
     * @return
     */
    @FormUrlEncoded
    @POST("emailcheck")
    suspend fun checkIfEmailExists(
        @Field("email") email: String,
        @Tag authorization: AuthorizationType = AuthorizationType.NONE,
    ): EmailCheckModel

I have tried to add various rules to try out, this is what it is for now - https://pastebin.com/zJPcJFYT Also trying out @Keep on my models and apiService.

DarkAbhi avatar Apr 27 '23 16:04 DarkAbhi

@DarkAbhi Do you have a custom CallAdapter set to retrofit?

eury-fsl avatar Apr 27 '23 16:04 eury-fsl

@eury-fsl No. This is how I provide my retrofit instances

@Provides
    @Singleton
    @Keep
    fun getIkuRetrofit(moshi: Moshi, okHttpClient: OkHttpClient): IkuApiService {
        return Retrofit.Builder()
            .baseUrl(Constants.IKU_BASE_URL)
            .addConverterFactory(MoshiConverterFactory.create(moshi))
            .client(okHttpClient)
            .build()
            .create(IkuApiService::class.java)
    }

    @Provides
    @Singleton
    @Keep
    fun getIkuAuthApiRetrofit(
        moshi: Moshi,
        @Named("for_auth") okHttpClient: OkHttpClient,
    ): IkuAuthApiService {
        return Retrofit.Builder()
            .baseUrl(Constants.IKU_BASE_URL)
            .addConverterFactory(MoshiConverterFactory.create(moshi))
            .client(okHttpClient)
            .build()
            .create(IkuAuthApiService::class.java)

DarkAbhi avatar Apr 27 '23 16:04 DarkAbhi

After clearing cache and invalidating AS, it now worked :)

DarkAbhi avatar Apr 27 '23 20:04 DarkAbhi

Even putting the lines below on Proguard:

# -dontwarn kotlin.coroutines.Continuation
-dontwarn okhttp3.internal.platform.**
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
-dontwarn org.openjsse.**

# TODO: Waiting for new retrofit release to remove these rules
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
-keep,allowobfuscation,allowshrinking class retrofit2.Response
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation

I still have the issue

Missing class kotlin.coroutines.Continuation (referenced from: java.lang.Object retrofit2.HttpServiceMethod$SuspendForBody.adapt(retrofit2.Call, java.lang.Object[]) and 2 other contexts)

My project doesn't uses Kotlin, it is pure Java 1.8, and it was working before.

skhaz avatar May 01 '23 12:05 skhaz

Even putting the lines below on Proguard:

# -dontwarn kotlin.coroutines.Continuation
-dontwarn okhttp3.internal.platform.**
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
-dontwarn org.openjsse.**

# TODO: Waiting for new retrofit release to remove these rules
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
-keep,allowobfuscation,allowshrinking class retrofit2.Response
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation

I still have the issue

Missing class kotlin.coroutines.Continuation (referenced from: java.lang.Object retrofit2.HttpServiceMethod$SuspendForBody.adapt(retrofit2.Call, java.lang.Object[]) and 2 other contexts)

My project doesn't uses Kotlin, it is pure Java 1.8, and it was working before.

it's a issue with R8 full mode not with java version and with new version of android studio R8 full mode is enabled by default.

android.enableR8.fullMode=false

add this line to your gradle.properties file and it will be working perfectly.

examgoal avatar May 05 '23 06:05 examgoal

it's a issue with R8 full mode not with java version and with new version of android studio R8 full mode is enabled by default. android.enableR8.fullMode=false

Full mode is there for a reason though. Don't opt out of it when the fix is simply to add a couple more rules to your proguard file

StylianosGakis avatar May 05 '23 09:05 StylianosGakis

New proguard rules added but not published yet, you can add these rules to your proguard-rules files.

https://github.com/square/retrofit/blob/6cd6f7d8287f73909614cb7300fcde05f5719750/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro#L34-L41

See #3579 & #3598.

Wonder why it stopped getting rules from the library - adding these rules manually solved the issue to me.

PhilipDukhov avatar May 05 '23 11:05 PhilipDukhov

New proguard rules added but not published yet, you can add these rules to your proguard-rules files. https://github.com/square/retrofit/blob/6cd6f7d8287f73909614cb7300fcde05f5719750/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro#L34-L41

See #3579 & #3598.

Wonder why it stopped getting rules from the library - adding these rules manually solved the issue to me.

Even adding these rules, doesn't worked for me (I'm using Java, not Kotlin)

skhaz avatar May 06 '23 19:05 skhaz

Update: Adding -dontwarn kotlin.coroutines.Continuation to proguard's file solved the issue for me.

skhaz avatar May 06 '23 20:05 skhaz