java.lang.NoClassDefFoundError: com.example.api.retrofit.AuthenticationInterceptor$intercept$1

Viewed 462

I recently added retrofit2 to my app. It works well on a Nexus 6P running Android 8.0, but when testing on an older OnePlus One, running Android 6.0.1, I ran into an error:

java.lang.NoClassDefFoundError: com.example.api.retrofit.AuthenticationInterceptor$intercept$1
    at com.example.api.retrofit.AuthenticationInterceptor.intercept(AuthenticationInterceptor.kt:21)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
    at okhttp3.RealCall.execute(RealCall.java:69)
    at retrofit2.OkHttpCall.execute(OkHttpCall.java:180)
    at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:91)
    at com.example.fragment.xx.XXFragment$onViewCreated$2.getItemsForConstraint(XXFragment.kt:53)
    at com.example.view.MyAutoCompleteTextView$Adapter$getFilter$1.performFiltering(MyAutoCompleteTextView.kt:114)
    at android.widget.Filter$RequestHandler.handleMessage(Filter.java:234)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:148)
    at android.os.HandlerThread.run(HandlerThread.java:61)

Here is my code:

build.gradle

buildscript {
    ext {
        okhttpVersion = '3.8.0'
        retrofitVersion = '2.3.0'
    }
...
}

app/build.gradle:

compile "com.squareup.okhttp3:okhttp:$okhttpVersion"
compile "com.squareup.okhttp3:logging-interceptor:$okhttpVersion"
compile 'com.google.code.gson:gson:2.8.1'
compile("com.squareup.retrofit2:retrofit:$retrofitVersion") {
    exclude module: 'okhttp'
}
compile "com.squareup.retrofit2:converter-gson:$retrofitVersion"

Building the service:

OkHttpClient client = new OkHttpClient().newBuilder()
        .addInterceptor(new AuthenticationInterceptor(ctx))
        .addInterceptor(new HttpLoggingInterceptor()
                .setLevel(HttpLoggingInterceptor.Level.BODY))
        .build();
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(HOST + VERSION + "/")
        .client(client)
        .addConverterFactory(new EnvelopingConverter())
        .addConverterFactory(GsonConverterFactory.create())
        .build();
retrofitService = retrofit.create(ApiService.class);

The interceptor:

import android.content.Context
import com.example.api.Api
import com.example.controller.AuthManager
import okhttp3.Interceptor
import okhttp3.Response

class AuthenticationInterceptor(val context: Context) : Interceptor {
    override fun intercept(chain: Interceptor.Chain?): Response? {
        var request = chain?.request()
        val headers = AuthManager.getAuthHeaders(context)

        if (request != null) {
            val requestBuilder = request.newBuilder()
            requestBuilder.addHeader(Api.HEADER_ACCEPT, Api.HEADER_APP_JSON)

            headers.forEach { key, value ->  // this is line 21
                requestBuilder.addHeader(key, value)
            }

            request = requestBuilder.build()
        }

        return chain?.proceed(request)
    }
}

I'm not sure what the issue might be? Anyone has any ideas?

UPDATE:

Wrote the interceptor in java and it seems to work. Why? I would really want to stick to Kotlin for this project.

public class AuthInterceptor implements Interceptor {
    private Context context;

    public AuthInterceptor(Context ctx) {
        context = ctx;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Map<String, String> headers = AuthManager.getAuthHeaders(context);
        if (request != null) {
            Request.Builder requestBuilder = request.newBuilder();
            requestBuilder.addHeader(Api.HEADER_ACCEPT, Api.HEADER_APP_JSON);
            for (String key : headers.keySet()) {
                requestBuilder.addHeader(key, headers.get(key));
            }
            request = requestBuilder.build();
        }
        return chain.proceed(request);
    }
}
2 Answers

After a couple of hours of debugging, I figured out what was going on:

In my gradle file I also had compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"

Because of this, it allowed me to use the following code:

headers.forEach { key, value ->  // this is line 21
    requestBuilder.addHeader(key, value)
}

When decompiling and looking at the java code, this will look like this:

e.forEach((BiConsumer)(new BiConsumer() {
           // $FF: synthetic method
           // $FF: bridge method
           public void accept(Object var1, Object var2) {
              this.accept((String)var1, (String)var2);
           }
           public final void accept(String key, String value) {
              requestBuilder.addHeader(key, value);
           }
        }));

The problem here is that the class BiConsumer was added in API level 24. That's why it worked on Android 8.0(API level 26) and it didn't work on Android 6.0.1(API level 23).

So, I ended up using a regular for loop instead of the forEach I went for initially.

The main problem here is the exception: java.lang.NoClassDefFoundError: com.example.api.retrofit.AuthenticationInterceptor$intercept$1. If it would have said java.lang.NoClassDefFoundError: java.util.function.BiConsumer, I think it would have been much easier to debug.

Related