OkHttp3 从不在慢速互联网上超时
OkHttp3 Never Timeout on slow internet
首先,我已经阅读了很多关于我的问题的问题,但它从来没有给我解决方案。以下是我阅读的关于我的问题的一些问题。
- Question 1
- Question 2
- Qusetion 3
- Question 4
- Question 5
- Question 6
关于我的问题,我也阅读了这个article,但它也没有给我提供解决方案。
问题:
我在 Web 服务应用程序中使用 Okhhtp3 库。它工作正常,但当互联网连接缓慢或连接不可靠时,它会卡住并且永远不会超时或永远不会调用超时异常或失败方法。
客户端代码如下:
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
如何在 20 秒后获取超时异常或调用失败方法?
请帮帮我。谢谢
正如 Trevor Halvorson 所指出的,您可以在客户端构建器中设置 callTimeout
,方法如下:
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.callTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
我在虚拟项目中使用 okhttp3
的 3.14.0
版本进行了个人测试:
implementation 'com.squareup.okhttp3:okhttp:3.14.0'
并将超时设置为 5 秒,我的模拟器 连接 为 GPRS 和 差 连通性 我得到
java.net.SocketExcpetion: Socket closed: timeout
这是我完整的假人activity:
package com.example.shadowsheep.myapplication;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.concurrent.TimeUnit;
import androidx.appcompat.app.AppCompatActivity;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView helloTextView = findViewById(R.id.helloTextView);
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.callTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
Request request = new Request.Builder()
.url("https://www.versionestabile.it/blog")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
Log.d("OKHTTP3", e.getMessage());
// You get this failure
runOnUiThread(() -> helloTextView.setText("TIMEOUT - FAILURE -> " + e.getMessage()));
}
@Override
public void onResponse(Call call, Response response) throws IOException {
try {
final String _body = response.body().string();
Log.d("OKHTTP3", _body);
runOnUiThread(() -> {
helloTextView.setText(_body);
});
} catch (InterruptedIOException e) {
runOnUiThread(() -> {
// Or this exception depending when timeout is reached
helloTextView.setText("TIMEOUT EXCEPTION->"+ e.getCause() + ": " + e.getMessage());
});
}
}
});
}
}
我也会给你我的应用 build.gradle
文件。
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.example.shadowsheep.myapplication"
minSdkVersion 24
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0-alpha03'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.2-alpha02'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha02'
implementation 'com.squareup.okhttp3:okhttp:3.14.0'
}
我的测试用例也不行,每次都需要 30+ 秒:
- Android 模拟器 Nexus 4(Android 版本 4.1.2)
- Windows 7 With no internet connection (确保模拟器没有网络连接)
gradle:
implementation 'com.squareup.okhttp3:okhttp:3.12.2'
defaultConfig {
minSdkVersion 16
targetSdkVersion 26
}
compileSdkVersion 27
buildToolsVersion '28.0.3'
java:
public static class CheckIpAsyncTask extends util.android.os.AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
try {
Logger.i(TAG, "API::FBD::checkIpInfo, API_IPINFO = " + API_IPINFO);
if (BuildConfig.DEBUG) {
//ThreadUtil.sleep(10 * TimeUtil.SECOND);
}
Request req = new Request.Builder()
.get()
.url(API_IPINFO)
.build();
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(2, TimeUnit.SECONDS)
.callTimeout(2, TimeUnit.SECONDS)
.readTimeout(2, TimeUnit.SECONDS)
.writeTimeout(2, TimeUnit.SECONDS)
.build();
Response response = client.newCall(req).execute();
mHttpCode = response.code();
if (mHttpCode == HttpURLConnection.HTTP_OK) {
boolean appConnected = ServerHelper.checkAppConnected();
if (!appConnected) {
JSONObject res = new JSONObject(response.body().string().trim());
mCountry = res.getString("country").toLowerCase();
mIp = res.getString("ip").toLowerCase();
Logger.i(TAG, "API::FBD::checkIpInfo, res = " + res);
PreferenceHelper.get(mContext).setIpInfoApiCountry(mCountry);
PreferenceHelper.get(mContext).setIpInfoApiHost(mIp);
}
}
} catch (java.io.InterruptedIOException interruptedIOException) {
Logger.e(TAG, "API::FBD::checkIpInfo, InterruptedIOException=" + interruptedIOException);
} catch (Throwable ignored) {
Logger.e(TAG, "API::FBD::checkIpInfo, Throwable=" + ignored);
}
return null;
}
日志:
04-04 06:25:06.940 4492-4645/? I/Toolbox: [PLACE] at com.xy.ui.FbdUtil$CheckIpAsyncTask.doInBackground(FbdUtil.java:98)
[FbdUtil]API::FBD::checkIpInfo, API_IPINFO = http://api.z.com:30070/servers/ipinfo
04-04 06:25:34.960 4492-4645/? E/Toolbox: [PLACE] at com.xy.ui.FbdUtil$CheckIpAsyncTask.doInBackground(FbdUtil.java:129)
[FbdUtil]API::FBD::checkIpInfo, InterruptedIOException=java.io.InterruptedIOException: timeout
模拟器 Nexus 6(Android 8.0 版)上的日志:
04-04 06:52:44.696 2624-3950/? I/Toolbox: [PLACE] at com.xy.ui.FbdUtil$CheckIpAsyncTask.doInBackground(FbdUtil.java:98)
[FbdUtil]API::FBD::checkIpInfo, API_IPINFO = http://api.z.com:30070/servers/ipinfo
04-04 06:53:59.886 2624-3175/? E/Toolbox: [PLACE] at com.xy.ui.ForbiddenUtil$CheckIpAsyncTask.doInBackground(FbdUtil.java:129)
[FbdUtil]API::FBD::checkIpInfo, InterruptedIOException=java.io.InterruptedIOException: timeout
最后,Dmitry Zenovich 在 'Question 6' 中的解决方案对我有用:
.dns(hostname -> Single.fromCallable(() -> Arrays.asList(InetAddress.getAllByName(hostname)))
.timeout(2, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.onErrorReturnItem(new ArrayList<>())
.blockingGet())
首先,我已经阅读了很多关于我的问题的问题,但它从来没有给我解决方案。以下是我阅读的关于我的问题的一些问题。
- Question 1
- Question 2
- Qusetion 3
- Question 4
- Question 5
- Question 6
关于我的问题,我也阅读了这个article,但它也没有给我提供解决方案。
问题:
我在 Web 服务应用程序中使用 Okhhtp3 库。它工作正常,但当互联网连接缓慢或连接不可靠时,它会卡住并且永远不会超时或永远不会调用超时异常或失败方法。
客户端代码如下:
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
如何在 20 秒后获取超时异常或调用失败方法?
请帮帮我。谢谢
正如 Trevor Halvorson 所指出的,您可以在客户端构建器中设置 callTimeout
,方法如下:
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.callTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
我在虚拟项目中使用 okhttp3
的 3.14.0
版本进行了个人测试:
implementation 'com.squareup.okhttp3:okhttp:3.14.0'
并将超时设置为 5 秒,我的模拟器 连接 为 GPRS 和 差 连通性 我得到
java.net.SocketExcpetion: Socket closed: timeout
这是我完整的假人activity:
package com.example.shadowsheep.myapplication;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.concurrent.TimeUnit;
import androidx.appcompat.app.AppCompatActivity;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView helloTextView = findViewById(R.id.helloTextView);
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.callTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
Request request = new Request.Builder()
.url("https://www.versionestabile.it/blog")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
Log.d("OKHTTP3", e.getMessage());
// You get this failure
runOnUiThread(() -> helloTextView.setText("TIMEOUT - FAILURE -> " + e.getMessage()));
}
@Override
public void onResponse(Call call, Response response) throws IOException {
try {
final String _body = response.body().string();
Log.d("OKHTTP3", _body);
runOnUiThread(() -> {
helloTextView.setText(_body);
});
} catch (InterruptedIOException e) {
runOnUiThread(() -> {
// Or this exception depending when timeout is reached
helloTextView.setText("TIMEOUT EXCEPTION->"+ e.getCause() + ": " + e.getMessage());
});
}
}
});
}
}
我也会给你我的应用 build.gradle
文件。
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.example.shadowsheep.myapplication"
minSdkVersion 24
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0-alpha03'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.2-alpha02'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha02'
implementation 'com.squareup.okhttp3:okhttp:3.14.0'
}
我的测试用例也不行,每次都需要 30+ 秒:
- Android 模拟器 Nexus 4(Android 版本 4.1.2)
- Windows 7 With no internet connection (确保模拟器没有网络连接)
gradle:
implementation 'com.squareup.okhttp3:okhttp:3.12.2'
defaultConfig {
minSdkVersion 16
targetSdkVersion 26
}
compileSdkVersion 27
buildToolsVersion '28.0.3'
java:
public static class CheckIpAsyncTask extends util.android.os.AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
try {
Logger.i(TAG, "API::FBD::checkIpInfo, API_IPINFO = " + API_IPINFO);
if (BuildConfig.DEBUG) {
//ThreadUtil.sleep(10 * TimeUtil.SECOND);
}
Request req = new Request.Builder()
.get()
.url(API_IPINFO)
.build();
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(2, TimeUnit.SECONDS)
.callTimeout(2, TimeUnit.SECONDS)
.readTimeout(2, TimeUnit.SECONDS)
.writeTimeout(2, TimeUnit.SECONDS)
.build();
Response response = client.newCall(req).execute();
mHttpCode = response.code();
if (mHttpCode == HttpURLConnection.HTTP_OK) {
boolean appConnected = ServerHelper.checkAppConnected();
if (!appConnected) {
JSONObject res = new JSONObject(response.body().string().trim());
mCountry = res.getString("country").toLowerCase();
mIp = res.getString("ip").toLowerCase();
Logger.i(TAG, "API::FBD::checkIpInfo, res = " + res);
PreferenceHelper.get(mContext).setIpInfoApiCountry(mCountry);
PreferenceHelper.get(mContext).setIpInfoApiHost(mIp);
}
}
} catch (java.io.InterruptedIOException interruptedIOException) {
Logger.e(TAG, "API::FBD::checkIpInfo, InterruptedIOException=" + interruptedIOException);
} catch (Throwable ignored) {
Logger.e(TAG, "API::FBD::checkIpInfo, Throwable=" + ignored);
}
return null;
}
日志:
04-04 06:25:06.940 4492-4645/? I/Toolbox: [PLACE] at com.xy.ui.FbdUtil$CheckIpAsyncTask.doInBackground(FbdUtil.java:98)
[FbdUtil]API::FBD::checkIpInfo, API_IPINFO = http://api.z.com:30070/servers/ipinfo
04-04 06:25:34.960 4492-4645/? E/Toolbox: [PLACE] at com.xy.ui.FbdUtil$CheckIpAsyncTask.doInBackground(FbdUtil.java:129)
[FbdUtil]API::FBD::checkIpInfo, InterruptedIOException=java.io.InterruptedIOException: timeout
模拟器 Nexus 6(Android 8.0 版)上的日志:
04-04 06:52:44.696 2624-3950/? I/Toolbox: [PLACE] at com.xy.ui.FbdUtil$CheckIpAsyncTask.doInBackground(FbdUtil.java:98)
[FbdUtil]API::FBD::checkIpInfo, API_IPINFO = http://api.z.com:30070/servers/ipinfo
04-04 06:53:59.886 2624-3175/? E/Toolbox: [PLACE] at com.xy.ui.ForbiddenUtil$CheckIpAsyncTask.doInBackground(FbdUtil.java:129)
[FbdUtil]API::FBD::checkIpInfo, InterruptedIOException=java.io.InterruptedIOException: timeout
最后,Dmitry Zenovich 在 'Question 6' 中的解决方案对我有用:
.dns(hostname -> Single.fromCallable(() -> Arrays.asList(InetAddress.getAllByName(hostname)))
.timeout(2, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.onErrorReturnItem(new ArrayList<>())
.blockingGet())