本文主要应用了OkHttp的Interceptor来实现自定义重试次数
虽然OkHttp自带retryOnConnectionFailure(true)方法可以实现重试,但是不支持自定义重试次数,所以有时并不能满足我们的需求。
#1.自定义重试拦截器:
/** * 重试拦截器 */ public class RetryIntercepter implements Interceptor { public int maxRetry;//最大重试次数 private int retryNum = 0;//假如设置为3次重试的话,则最大可能请求4次(默认1次+3次重试) public RetryIntercepter(int maxRetry) { this.maxRetry = maxRetry; } @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); System.out.println("retryNum=" + retryNum); Response response = chain.proceed(request); while (!response.isSuccessful() && retryNum < maxRetry) { retryNum++; System.out.println("retryNum=" + retryNum); response = chain.proceed(request); } return response; } }
#2.测试场景类:
1 public class RetryTest { 2 String mUrl = "https://www.baidu.com/"; 3 OkHttpClient mClient; 4 5 @Before 6 public void setUp() { 7 HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); 8 logging.setLevel(HttpLoggingInterceptor.Level.BODY); 9 10 mClient = new OkHttpClient.Builder() 11 .addInterceptor(new RetryIntercepter(3))//重试 12 .addInterceptor(logging)//网络日志 13 .addInterceptor(new TestInterceptor())//模拟网络请求 14 .build(); 15 } 16 17 @Test 18 public void testRequest() throws IOException { 19 Request request = new Request.Builder() 20 .url(mUrl) 21 .build(); 22 Response response = mClient.newCall(request).execute(); 23 System.out.println("onResponse:" + response.body().string()); 24 } 25 26 class TestInterceptor implements Interceptor { 27 28 @Override 29 public Response intercept(Chain chain) throws IOException { 30 Request request = chain.request(); 31 String url = request.url().toString(); 32 System.out.println("url=" + url); 33 Response response = null; 34 if (url.equals(mUrl)) { 35 String responseString = "{"message":"我是模拟的数据"}";//模拟的错误的返回值 36 response = new Response.Builder() 37 .code(400) 38 .request(request) 39 .protocol(Protocol.HTTP_1_0) 40 .body(ResponseBody.create(MediaType.parse("application/json"), responseString.getBytes())) 41 .addHeader("content-type", "application/json") 42 .build(); 43 } else { 44 response = chain.proceed(request); 45 } 46 return response; 47 } 48 } 49 50 }
#3.输出结果:
1 retryNum=0 2 --> GET https://www.baidu.com/ HTTP/1.1 3 --> END GET 4 url=https://www.baidu.com/ 5 <-- 400 null https://www.baidu.com/ (13ms) 6 content-type: application/json 7 8 {"message":"我是模拟的数据"} 9 <-- END HTTP (35-byte body) 10 retryNum=1 11 --> GET https://www.baidu.com/ HTTP/1.1 12 --> END GET 13 url=https://www.baidu.com/ 14 <-- 400 null https://www.baidu.com/ (0ms) 15 content-type: application/json 16 17 {"message":"我是模拟的数据"} 18 <-- END HTTP (35-byte body) 19 retryNum=2 20 --> GET https://www.baidu.com/ HTTP/1.1 21 --> END GET 22 url=https://www.baidu.com/ 23 <-- 400 null https://www.baidu.com/ (0ms) 24 content-type: application/json 25 26 {"message":"我是模拟的数据"} 27 <-- END HTTP (35-byte body) 28 retryNum=3 29 --> GET https://www.baidu.com/ HTTP/1.1 30 --> END GET 31 url=https://www.baidu.com/ 32 <-- 400 null https://www.baidu.com/ (0ms) 33 content-type: application/json 34 35 {"message":"我是模拟的数据"} 36 <-- END HTTP (35-byte body) 37 onResponse:{"message":"我是模拟的数据"}
#4.结果分析:
>1. 这里我用一个TestInterceptor拦截器拦截掉真实的网络请求,实现response.code的自定义
2. 在RetryIntercepter中,通过response.isSuccessful()来对响应码进行判断,循环调用了多次chain.proceed(request)来实现重试拦截
3. 从输出中可以看到,一共请求了4次(默认1次+重试3次)。
#5.其它实现方式
如果你是使用OkHttp+Retrofit+RxJava,你也可以使用retryWhen操作符:retryWhen(new RetryWithDelay())来实现重试机制
1 public class RetryWithDelay implements Func1<Observable<? extends Throwable>, Observable<?>> { 2 3 private final int maxRetries; 4 private final int retryDelayMillis; 5 private int retryCount; 6 7 public RetryWithDelay(int maxRetries, int retryDelayMillis) { 8 this.maxRetries = maxRetries; 9 this.retryDelayMillis = retryDelayMillis; 10 } 11 12 @Override 13 public Observable<?> call(Observable<? extends Throwable> attempts) { 14 return attempts 15 .flatMap(new Func1<Throwable, Observable<?>>() { 16 @Override 17 public Observable<?> call(Throwable throwable) { 18 if (++retryCount <= maxRetries) { 19 // When this Observable calls onNext, the original Observable will be retried (i.e. re-subscribed). 20 LogUtil.print("get error, it will try after " + retryDelayMillis + " millisecond, retry count " + retryCount); 21 return Observable.timer(retryDelayMillis, 22 TimeUnit.MILLISECONDS); 23 } 24 // Max retries hit. Just pass the error along. 25 return Observable.error(throwable); 26 } 27 }); 28 } 29 }