In this paper, OkHttp's Interceptor is used to implement a custom number of retries
Although OkHttp has its own retryOnConnectionFailure(true) method to enable retries, it does not support custom retries, so sometimes it does not meet our needs.
#1. Custom retry interceptor:
/** * Retry Interceptor */ public class RetryIntercepter implements Interceptor { public int maxRetry;//max retries private int retryNum = 0;//If set to 3 retries, the maximum possible request is 4 (default 1)+3 Second retry) 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. Test scenario class:
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))//retry 12 .addInterceptor(logging)//Network Log 13 .addInterceptor(new TestInterceptor())//Simulate network requests 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\":\"I am analogue data\"}";//Return value of simulated error 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. Output results:
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":"I am analogue data"} 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":"I am analogue data"} 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":"I am analogue data"} 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":"I am analogue data"} 36 <-- END HTTP (35-byte body) 37 onResponse:{"message":"I am analogue data"}
#4. Result analysis:
>1. Here I use a TestInterceptor interceptor to intercept real network requests and customize response.code
2. In RetryIntercepter, the response code is judged by response.isSuccessful(), and chain.proceed(request) is called repeatedly to intercept retries
3. As you can see from the output, there are four requests (default 1 + retry 3).
#5. Other implementations
If you are using OkHttp+Retrofit+RxJava, you can also use the retryWhen operator: retryWhen(new RetryWithDelay()) to implement the retry mechanism
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 }