square / retrofit

A type-safe HTTP client for Android and the JVM
https://square.github.io/retrofit/
Apache License 2.0
43.01k stars 7.3k forks source link

ClassCastException thrown from unused Retrofit services with R8 #4134

Open grandstaish opened 5 months ago

grandstaish commented 5 months ago

Bit of an edge case here—but if all of the functions in a Retrofit interface are unused, R8 will strip them. This causes the following Proguard rule to miss: https://github.com/square/retrofit/blob/dfdad425e9d1c47dbce335c9808c75c72ef912cc/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro#L31

Then once you try to instantiate the stripped service class, you get a fairly nondescript ClassCastException from Retrofit. E.g.:

Caused by: java.lang.ClassCastException
  at androidx.appcompat.app.ToolbarActionBar$$ExternalSyntheticThrowCCEIfNotNull0.m(:0)

Replacing the referenced rule with the following one fixes the issue for us:

-if interface *
-keepclasseswithmembers,allowobfuscation interface <1> {
  @retrofit2.http.* <methods>;
}

(Source: here)

Note: I barely know how to proguard, so this definitely requires some scrutiny. Raising issue here for visibility.

aroyarexs commented 4 months ago

I think we have a problem which relates to yours: https://stackoverflow.com/questions/78518260/r8-shrinker-removes-retrofit-interfaces The difference is that we are using the methods of the interface. The methods are not unused.

But instead of using our instance that we created using Retrofit.create(Class<T> service), it somehow uses/references our demo implementation which implements our retrofit interface ... 🤷‍♂️ Adding @Keep rules to the interfaces seems to fix the issue like suggested in your source.

Soltus commented 4 months ago

Debug mode works fine, but once released mode causes very stubborn java.lang. ClassCastException

glovebx commented 3 months ago

Debug mode works fine, but once released mode causes very stubborn java.lang. ClassCastException

Add @Keep annotation to the interface solved my problem

Soltus commented 3 months ago

我的问题得到了解决,方法是与服务端交互的JSON对象类不混淆,例如: My problem is solved by not confusing JSON object classes that interact with the server level, for example:

-keep class sc.windom.sofill.dataClass.** { *; }