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);
}
});
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.
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
}
});
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
来源:https://stackoverflow.com/questions/55547248/retrofit-not-working-on-specific-versions-of-android