retrofit icon indicating copy to clipboard operation
retrofit copied to clipboard

R8 full mode and @Streaming giving runtime error with minifyEnabled

Open Stofferoo opened this issue 1 year ago • 5 comments

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 <ResponseBody> 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 ?

Stofferoo avatar May 17 '23 07:05 Stofferoo

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.

JakeWharton avatar May 17 '23 11:05 JakeWharton

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

Stofferoo avatar May 17 '23 12:05 Stofferoo

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>

AndroidPat avatar May 23 '23 13:05 AndroidPat

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.

qaszxcwer avatar Jun 07 '23 14:06 qaszxcwer

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:

  • This works on debug builds (minifyEnbaled = false)
  • This does not works on signed builds (minifyEnbaled = true). Note: Tried both fullR8Support enabled and disabled.
  • This works on signed builds (minifyEnbaled = true) if I add the HttpLoggingInterceptor

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 😅

Alexs784 avatar Jun 21 '23 14:06 Alexs784