AGP 7+ / JDK 11 / release: ClassNotFoundException: Didn't find class "okhttp3.MultipartBody$Part" on path: DexPathList

Viewed 240

As the title says, I'm having some issue on a project since we updated to Java 11 and to AGP 7+. The issue only happens in release and when we make a retrofit call using okhttp3.MultipartBody :

2022-01-18 00:56:23.043 27908-27908/com.test.okhttpissue E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.test.okhttpissue, PID: 27908
    java.lang.TypeNotPresentException: Type okhttp3.MultipartBody$Part not present
        at libcore.reflect.ParameterizedTypeImpl.getRawType(ParameterizedTypeImpl.java:67)
        at libcore.reflect.ParameterizedTypeImpl.getResolvedType(ParameterizedTypeImpl.java:76)
        at libcore.reflect.GenericArrayTypeImpl.getGenericComponentType(GenericArrayTypeImpl.java:32)
        at j2.y.j(:362)
        at j2.s$a.j(:812)
        at j2.s$a.g(:626)
        at j2.s$a.f(:325)
        at j2.s$a.b(:206)
        at j2.s.b(:67)
        at j2.v.b(:26)
        at j2.u.c(:202)
        at j2.u$a.invoke(:160)
        at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
        at $Proxy2.a(Unknown Source)
        at f1.a.a(:41)
        at com.test.okhttpissue.MainActivity.G(:30)
        at com.test.okhttpissue.MainActivity.F(Unknown Source:0)
        at f1.b.onClick(Unknown Source:0)
        at android.view.View.performClick(View.java:7259)
        at android.view.View.performClickInternal(View.java:7236)
        at android.view.View.access$3600(View.java:801)
        at android.view.View$PerformClick.run(View.java:27896)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7397)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
     Caused by: java.lang.ClassNotFoundException: okhttp3.MultipartBody$Part
        at java.lang.Class.classForName(Native Method)
        at java.lang.Class.forName(Class.java:454)
        at libcore.reflect.ParameterizedTypeImpl.getRawType(ParameterizedTypeImpl.java:65)
        at libcore.reflect.ParameterizedTypeImpl.getResolvedType(ParameterizedTypeImpl.java:76) 
        at libcore.reflect.GenericArrayTypeImpl.getGenericComponentType(GenericArrayTypeImpl.java:32) 
        at j2.y.j(:362) 
        at j2.s$a.j(:812) 
        at j2.s$a.g(:626) 
        at j2.s$a.f(:325) 
        at j2.s$a.b(:206) 
        at j2.s.b(:67) 
        at j2.v.b(:26) 
        at j2.u.c(:202) 
        at j2.u$a.invoke(:160) 
        at java.lang.reflect.Proxy.invoke(Proxy.java:1006) 
        at $Proxy2.a(Unknown Source) 
        at f1.a.a(:41) 
        at com.test.okhttpissue.MainActivity.G(:30) 
        at com.test.okhttpissue.MainActivity.F(Unknown Source:0) 
        at f1.b.onClick(Unknown Source:0) 
        at android.view.View.performClick(View.java:7259) 
        at android.view.View.performClickInternal(View.java:7236) 
        at android.view.View.access$3600(View.java:801) 
        at android.view.View$PerformClick.run(View.java:27896) 
        at android.os.Handler.handleCallback(Handler.java:883) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.app.ActivityThread.main(ActivityThread.java:7397) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935) 
     Caused by: java.lang.ClassNotFoundException: Didn't find class "okhttp3.MultipartBody$Part" on path: DexPathList[[zip file "/data/app/com.test.okhttpissue-pBclZ8Dcl_TuV_j7iAp7oA==/base.apk"],nativeLibraryDirectories=[/data/app/com.test.okhttpissue-pBclZ8Dcl_TuV_j7iAp7oA==/lib/arm64, /system/lib64, /system/product/lib64]]
        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:196)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
        at java.lang.Class.classForName(Native Method) 
        at java.lang.Class.forName(Class.java:454) 
        at libcore.reflect.ParameterizedTypeImpl.getRawType(ParameterizedTypeImpl.java:65) 
        at libcore.reflect.ParameterizedTypeImpl.getResolvedType(ParameterizedTypeImpl.java:76) 
        at libcore.reflect.GenericArrayTypeImpl.getGenericComponentType(GenericArrayTypeImpl.java:32) 
        at j2.y.j(:362) 
        at j2.s$a.j(:812) 
        at j2.s$a.g(:626) 
        at j2.s$a.f(:325) 
        at j2.s$a.b(:206) 
        at j2.s.b(:67) 
        at j2.v.b(:26) 
        at j2.u.c(:202) 
        at j2.u$a.invoke(:160) 
        at java.lang.reflect.Proxy.invoke(Proxy.java:1006) 
        at $Proxy2.a(Unknown Source) 
        at f1.a.a(:41) 
        at com.test.okhttpissue.MainActivity.G(:30) 
        at com.test.okhttpissue.MainActivity.F(Unknown Source:0) 
        at f1.b.onClick(Unknown Source:0) 
        at android.view.View.performClick(View.java:7259) 
        at android.view.View.performClickInternal(View.java:7236) 
        at android.view.View.access$3600(View.java:801) 
        at android.view.View$PerformClick.run(View.java:27896) 
        at android.os.Handler.handleCallback(Handler.java:883) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.app.ActivityThread.main(ActivityThread.java:7397) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935) 
2022-01-18 00:56:23.074 27908-27908/com.test.okhttpissue I/Process: Sending signal. PID: 27908 SIG: 9

The problem does not occur on AGP 4.2.2 / Java 1.8.

I was able to reproduce it in a basic sample app with the following configuration :

plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdkVersion 30

    defaultConfig {
        applicationId "com.test.okhttpissue"
        minSdk 24
        targetSdk 29
        versionCode 1
        versionName "1.0"
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            signingConfig signingConfigs.debug
            debuggable true
            minifyEnabled true
            shrinkResources true
            zipAlignEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_11
        targetCompatibility JavaVersion.VERSION_11
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_11.toString()
    }
    buildFeatures {
        viewBinding true
    }
}

dependencies {
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'com.google.android.material:material:1.0.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

    implementation "io.reactivex.rxjava3:rxjava:3.1.3"
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
    implementation "com.squareup.retrofit2:adapter-rxjava3:2.9.0"
    implementation "com.squareup.retrofit2:retrofit:2.9.0"
    implementation "com.squareup.retrofit2:converter-gson:2.9.0"
}

And the following snippet :

class IssueDemo {

    val testApi: TestApi by lazy {
        Retrofit.Builder()
            .baseUrl("https://google.com")
            .client(OkHttpClient().newBuilder().build())
            .addCallAdapterFactory(RxJava3CallAdapterFactory.createWithScheduler(Schedulers.io()))
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(TestApi::class.java)
    }

    interface TestApi {

        @Multipart
        @POST("test/files")
        fun uploadMultipleFiles(@Part files: Array<MultipartBody.Part?>): Single<String>
    }

    fun execute() {
        val file = File("")
        val requestFile: RequestBody =
            RequestBody.create(MediaType.parse("multipart/form-data"), file)
        val test = MultipartBody.Part.createFormData("nameFormData", file.name, requestFile)
        testApi.uploadMultipleFiles(arrayOf(test))
    }
}

I tried many things but so far the only thing that seems to fix it is to add a proguard rule -keep class okhttp3.MultipartBody but it doesn't feel right. With AGP 4.2.2 it was already using R8 and the proguard rules bundled with the library so why would we suddendly need another specific rule just for one class of this library?

Edit: It seems that switching from Array<MultipartBody.Part?> to List<MultipartBody.Part?>, without touching any proguard rule, solves the issue. Does someone understand what could be happening?

1 Answers

Possible that R8 can't detect the class being used as it may be invoked by reflection. If that is the case, you have to add explicit keep rule in proguard-project.txt.

Related