square / retrofit

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

R8 full mode and @Streaming giving runtime error with minifyEnabled #3898

Open Stofferoo opened 1 year ago

Stofferoo commented 1 year ago

Using Retrofit 2.9.0 and AGP 8.0.1 with fixes from https://github.com/square/retrofit/issues/3751 I still get one more error with R8 in full mode:

E  java.lang.IllegalArgumentException: Response must include generic type (e.g., Response<String>)
     for method ContentApi.getRawFile
        at retrofit2.Utils.methodError(SourceFile:54)
        at retrofit2.Utils.methodError(SourceFile:43)
        at retrofit2.HttpServiceMethod.parseAnnotations(SourceFile:77)
        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)
        at $Proxy23.getRawFile(Unknown Source)
        at com.kivra.android.repository.ContentFileDownloader.downloadFile(SourceFile:40)
        at com.kivra.android.repository.ContentPartFileTransform.fetchContentFiles(SourceFile:56)
        at com.kivra.android.repository.ContentPartFileTransform.access$fetchContentFiles(SourceFile:28)
        at com.kivra.android.repository.ContentPartFileTransform$transformData$2.invokeSuspend(SourceFile:42)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(SourceFile:33)
        at kotlinx.coroutines.DispatchedTask.run(SourceFile:106)
        at kotlinx.coroutines.internal.LimitedDispatcher.run(SourceFile:42)
        at kotlinx.coroutines.scheduling.TaskImpl.run(SourceFile:95)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(SourceFile:570)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(SourceFile:750)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(SourceFile:677)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(SourceFile:664)

This is how the signature looks like:

    @Streaming
    @GET("{apiVersion}/{actorType}/{actorKey}/content/{contentKey}/file/{fileKey}/raw/{fileName}")
    suspend fun getRawFile(
        @Path("apiVersion") apiVersion: String,
        @Path("actorType") actorType: String,
        @Path("actorKey") actorKey: String,
        @Path("contentKey") contentKey: String,
        @Path("fileKey") fileKey: String,
        @Path("fileName") fileName: String
    ): Response<ResponseBody>

My best guess is that is getting stripped away but I don't know how to configure to not remove it. I thought this would have done it but apparently not:

# R8 full mode strips generic signatures from return types if not kept.
-if interface * { @retrofit2.http.* public *** *(...); }
-keep,allowoptimization,allowshrinking,allowobfuscation class <3>

So perhaps something is missing still from this commit: https://github.com/square/retrofit/commit/59d302aedce5edae4806efac57b630d4fe8c27db ?

JakeWharton commented 1 year ago

That commit is not in the released version. Copy it into your project shrinker config and it should work until the next version is released.

Stofferoo commented 1 year ago

Ah sorry! I was meaning to say I added that bit locally and it did not fix this issue

AndroidPat commented 1 year ago

Keeping the replaced rules and adding the new ones from the commit https://github.com/square/retrofit/commit/59d302aedce5edae4806efac57b630d4fe8c27db fixed the issue for me.

# 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 interface retrofit2.Call
-keep,allowobfuscation,allowshrinking class retrofit2.Response

# R8 full mode strips generic signatures from return types if not kept.
-if interface * { @retrofit2.http.* public *** *(...); }
-keep,allowoptimization,allowshrinking,allowobfuscation class <3>
qaszxcwer commented 1 year ago

I have same problem when update AndroidStudio Flamingo & Gradle8.0.2 When i call a post method, crash: java.lang.IllegalArgumentException: Unable to create call adapter for interface w4.d

But i solve this, add "retrofit/src/main/resources/META-INF/proguard/retrofit2.pro" all code into my proguard-rules.pro file

Maybe we can wait for retrofit2 update new version to solve this problem totally.

Alexs784 commented 1 year ago

I am facing a similar issue with @Streaming annotated functions, not really a crash but just the app loading forever (only on minified builds). What is interesting to point out is:

Finally, I have 2 very similar interfaces that are @Streaming annotated, and one works while the other doesn't (on signed builds with minifyEnbaled = true). The working one is something like

    @Streaming
    @GET
    suspend fun downloadFile(@Url fileUrl: String): ResponseBody

The non-working one is something like:

    @Streaming
    @GET("api/{${MY_ID}}/bob")
    suspend fun downloadDocument(@Path(MY_ID) myId: String, @Query(TYPE) type: String): ResponseBody

The only difference I can spot between the two is the different types of params added?

Update: removing the @Streaming tag solves the issue 🤔 Although that could be explained by the network protocol not supporting it, it still doesn't explain why it works on debug builds or with the logging interceptor 😅