Retrofit not working on specific versions of android
Solution 1
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.
Solution 2
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
Solution 3
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
}
});
SaNtoRiaN
Interested in Java, android programming and learning new languages.
Updated on June 24, 2022Comments
-
SaNtoRiaN almost 2 years
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); } });
-
aha about 5 yearsThe last paragraph is wrong. You can instruct OkHttp to use
COMPATIBLE_TLS
, see github.com/square/okhttp/wiki/HTTPS. That should work with TLS 1.0. Beware that you use anything newer than OkHttp 3.12 because that's the last one that supports Android 4.x and earlier. -
cutiko about 5 yearsDocs developer.android.com/training/articles/security-gms-provider and full discussion github.com/square/okhttp/issues/2372 Maybe Im missing somethinf, be kind to point out please @MartinZeitler
-
madlymad about 5 yearsI would guess that @cutiko refers to the fact that before KitKat there wasn't support for TLS 1.2 medium.com/tech-quizlet/…
-
Martin Zeitler about 5 years@aha
MODERN_TLS
of3.12.x
still supportsTLSv1
, while in3.13.x
it had been added intoCOMPATIBLE_TLS
... so this problem only applies to the default configurationMODERN_TLS
. version3.13.x
should still work, while not using the default configuration; not that certain about3.14.x
. -
viper over 4 yearsOkay but we need this configuration only to connect with api without SSL (http)
-
Arpit J. almost 3 yearsAttribute usesCleartextTraffic is only used in API level 23 and higher