Home » Android » java – Retrofit not working on specific versions of android

java – Retrofit not working on specific versions of android

Posted by: admin June 15, 2020 Leave a comment

Questions:

I have a problem with Retrofit on my emulator running Android 4.3 and my device is on Android 4.4.2 while the same code runs normally on another emulator with Android 7.1.1

Each time I try to execute the get request I get a timeout exception.

java.net.SocketTimeoutException: failed to connect to jsonplaceholder.typicode.com/2606:4700:30::681c:3f5 (port 443) after 10000ms
        at libcore.io.IoBridge.connectErrno(IoBridge.java:159)
        at libcore.io.IoBridge.connect(IoBridge.java:112)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:459)
        at java.net.Socket.connect(Socket.java:842)
        at okhttp3.internal.platform.AndroidPlatform.connectSocket(AndroidPlatform.java:73)
        at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:246)
        at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:166)
        at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:257)
        at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135)
        at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114)
        at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:254)
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:200)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
        at java.lang.Thread.run(Thread.java:841)

The code is below

public interface Api {
    String BASE_URL = "https://jsonplaceholder.typicode.com/";

    @GET("posts")
    Call<ArrayList<Post>> getPosts();
}

and the call to the api

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(Api.BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build();

Api api = retrofit.create(Api.class);
Call<ArrayList<Post>> call = api.getPostes();
Log.i("RequestUrl", call.request().url().toString());
call.enqueue(new Callback<ArrayList<Post>>() {
    @Override
    public void onResponse(Call<ArrayList<Post>> call, Response<ArrayList<Post>> response) {
        mPostsList.setValue(response.body());
    }

    @Override
    public void onFailure(Call<ArrayList<Post>> call, Throwable t) {
        Log.e("Posts", "Error occurred", t);
    }
});
How to&Answers:

It reads java.net.SocketTimeoutException, which at first suggests to raise the client’s connect-timeout value, as it is being explained in this answer – but when reviewing the current source code of okhttp3.internal.platform.AndroidPlatform… this rather hints for incompatible protocols.

The server’s SSL certificate supports TLS 1.0, as it would be required for Android 4.x (there’s no problem on their side); the problem rather is, that the current version of OkHttp3 does not support TLS 1.0 anymore and therefore the handshake won’t ever take place (that’s why it throws such a misleading SocketTimeoutException instead of a SSLHandshakeException).


With OkHttp3 3.12.x, it should still be supported with the default configuration MODERN_TLS

but one could instruct OkHttp3 3.13.x to use configuration COMPATIBLE_TLS instead:

/* ConnectionSpec.MODERN_TLS is the default value */
List tlsSpecs = Arrays.asList(ConnectionSpec.MODERN_TLS);

/* providing backwards-compatibility for API lower than Lollipop: */
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
    tlsSpecs = Arrays.asList(ConnectionSpec.COMPATIBLE_TLS);
}

OkHttpClient client = new OkHttpClient.Builder()
    .connectionSpecs(tlsSpecs)
    .build();

One also has to set it as the client for Retrofit:

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(Api.BASE_URL)
    .addConverterFactory(GsonConverterFactory.create())
    .setClient(client)
    .build();

See the TLS Configuration History for the available protocol support, per OkHttp3 version. As it seems, 3.12.x even already supports TLS 1.3, as it will in future be required for Android Q. It might not even be required to down-grade OkHttp3, because MODERN_TLS of 3.12.x still supports TLSv1, while in 3.13.x it had been moved into COMPATIBLE_TLS; still uncertain about 3.14.x.

Even with current versions of OkHttp3, one could possibly still add the desired TLS 1.0 protocol back into ConnectionSpec.COMPATIBLE_TLS, since this is an ArrayList with a method .add() – without any guarantee, that there won’t be further incompatibilities; 3.12.x might still be the best choice for supporting Android 4.x onward and there might even be back-ports of newer features.

Answer:

If you use android 9 (Pie) or android SDK above 28 and get the issue on over the api call through Retrofit.

Add this line to your manifest android:usesCleartextTraffic="true"
Retrofit Issue

Answer:

Android prior to 21 has some missing SSL and Retrofit wont work. Using google services you can update the device protocols after that the HTTP request will work

    //compile 'com.google.android.gms:play-services-base:11.0.0'
     //remember to add the library in your dependencies

        //compile 'com.google.android.gms:play-services-base:$currentVersion'

        ProviderInstaller.installIfNeededAsync(this, new ProviderInstallListener() {

            @Override

            public void onProviderInstalled() {

                //Do your http request here

            }


            @Override

            public void onProviderInstallFailed(int errorCode, Intent recoveryIntent) {

                //sad face :C is sad

            }

        });