volley的重试机制及错误处理机制

71 篇文章 0 订阅
7 篇文章 0 订阅

当我们进行网络请求的时候,可能会面临很多复杂的环境。比如网络环境不好、服务器异常等。

所以当我们使用网络请求框架进行应用开发的时候,一个好的重试机制,可以让我们设定适当的重试次数,不会只请求一次或者一直重试请求无数次,以应对复杂的网络环境;一个好的错误处理机制可以让我们在出现错误的时候,及时作出反馈,不会一直在等待,使应用有一个友好的用户体验。


volley的重试机制的使用是比较简单。

我们可以通过设置RetryPolicy来设置volley的重试次数和请求超时时间。我们可以在使用该框架的时候这样进行设置:

request.setRetryPolicy(new DefaultRetryPolicy(20 *1000, 1, 1.0f));

可以看到DefaultRetryPolicy这个类的构造函数:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public DefaultRetryPolicy() {  
  2.         this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);  
  3.     }  

其中第一个参数就是设置超时的时间,第二个参数是设置重试的次数,第三个是一个基数时间,用于算两次重试请求之间的时间间隔。但我们没有进行参数的设置的时候,这三个参数都有一个默认的值,其中超时时间是2500ms,重试次数是1次,基数时间是1。



我们在app开发时这样简单的设置,但是,volley中是如何使用的呢,原理分析会在下面展示。

类:BasicNetWork.Java

该类中的performRequest()方法调用HttpStack进行网络请求,并对请求回来的结果进行解析封装,并返回最后的结果。其中重试机制和错误处理机制的使用也是在该方法中进行处理。

方法如下:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.     public NetworkResponse performRequest(Request<?> request) throws VolleyError {  
  3.         long requestStart = SystemClock.elapsedRealtime();  
  4.         while (true) {  
  5.             HttpResponse httpResponse = null;  
  6.             byte[] responseContents = null;  
  7.             Map<String, String> responseHeaders = new HashMap<String, String>();  
  8.             try {  
  9.                 // Gather headers.  
  10.                 Map<String, String> headers = new HashMap<String, String>();  
  11.                 addCacheHeaders(headers, request.getCacheEntry());  
  12.                 httpResponse = mHttpStack.performRequest(request, headers);  
  13.                 StatusLine statusLine = httpResponse.getStatusLine();  
  14.                 int statusCode = statusLine.getStatusCode();  
  15.   
  16.                 responseHeaders = convertHeaders(httpResponse.getAllHeaders());  
  17.                 // Handle cache validation.  
  18.                 if (statusCode == HttpStatus.SC_NOT_MODIFIED) {  
  19.                     return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,  
  20.                             request.getCacheEntry() == null ? null : request.getCacheEntry().data,  
  21.                             responseHeaders, true);  
  22.                 }  
  23.                   
  24.                 // Handle moved resources  
  25.                 if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {  
  26.                     String newUrl = responseHeaders.get("Location");  
  27.                     request.setRedirectUrl(newUrl);  
  28.                 }  
  29.   
  30.                 // Some responses such as 204s do not have content.  We must check.  
  31.                 if (httpResponse.getEntity() != null) {  
  32.                   responseContents = entityToBytes(httpResponse.getEntity());  
  33.                 } else {  
  34.                   // Add 0 byte response as a way of honestly representing a  
  35.                   // no-content request.  
  36.                   responseContents = new byte[0];  
  37.                 }  
  38.   
  39.                 // if the request is slow, log it.  
  40.                 long requestLifetime = SystemClock.elapsedRealtime() - requestStart;  
  41.                 logSlowRequests(requestLifetime, request, responseContents, statusLine);  
  42.   
  43.                 if (statusCode < 200 || statusCode > 299) {  
  44.                     throw new IOException();  
  45.                 }  
  46.                 return new NetworkResponse(statusCode, responseContents, responseHeaders, false);  
  47.             } catch (SocketTimeoutException e) {  
  48.                 attemptRetryOnException("socket", request, new TimeoutError());  
  49.             } catch (ConnectTimeoutException e) {  
  50.                 attemptRetryOnException("connection", request, new TimeoutError());  
  51.             } catch (MalformedURLException e) {  
  52.                 throw new RuntimeException("Bad URL " + request.getUrl(), e);  
  53.             } catch (IOException e) {  
  54.                 int statusCode = 0;  
  55.                 NetworkResponse networkResponse = null;  
  56.                 if (httpResponse != null) {  
  57.                     statusCode = httpResponse.getStatusLine().getStatusCode();  
  58.                 } else {  
  59.                     throw new NoConnectionError(e);  
  60.                 }  
  61.                 if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||   
  62.                         statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {  
  63.                     VolleyLog.e("Request at %s has been redirected to %s", request.getOriginUrl(), request.getUrl());  
  64.                 } else {  
  65.                     VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());  
  66.                 }  
  67.                 if (responseContents != null) {  
  68.                     networkResponse = new NetworkResponse(statusCode, responseContents,  
  69.                             responseHeaders, false);  
  70.                     if (statusCode == HttpStatus.SC_UNAUTHORIZED ||  
  71.                             statusCode == HttpStatus.SC_FORBIDDEN) {  
  72.                         attemptRetryOnException("auth",  
  73.                                 request, new AuthFailureError(networkResponse));  
  74.                     } else if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||   
  75.                                 statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {  
  76.                         attemptRetryOnException("redirect",  
  77.                                 request, new AuthFailureError(networkResponse));  
  78.                     } else {  
  79.                         // TODO: Only throw ServerError for 5xx status codes.  
  80.                         throw new ServerError(networkResponse);  
  81.                     }  
  82.                 } else {  
  83.                     throw new NetworkError(networkResponse);  
  84.                 }  
  85.             }  
  86.         }  
  87.     }  

可以分析,看到该方法中有一个while(true)的死循环,其实就是为了重试机制的使用(在请求的次数没有达到设置的次数时,会循环进行网络请求的操作)。

httpResponse = mHttpStack.performRequest(requestheaders);

这行代码就是真正的进行网络数据请求的代码,并返回得到的数据。然而在请求网络数据的时候,可能会出现异常,然后出现异常是就需要对异常进行处理。 这段代码会抓取SocketTimeoutException、ConnectTimeoutException、MalformedURLException、IOException这几个异常,其中在SocketTimeoutException和ConnectTimeoutException会进行重试的机制调用,就是调用attemptRetryOnException()这个方法。

方法如下:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private static void attemptRetryOnException(String logPrefix, Request<?> request,  
  2.            VolleyError exception) throws VolleyError {  
  3.        RetryPolicy retryPolicy = request.getRetryPolicy();  
  4.        int oldTimeout = request.getTimeoutMs();  
  5.   
  6.        try {  
  7.            retryPolicy.retry(exception);  
  8.        } catch (VolleyError e) {  
  9.            request.addMarker(  
  10.                    String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));  
  11.            throw e;  
  12.        }  
  13.        request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));  
  14.    }  

其中RetryPolicy  retryPolicy  =  request .getRetryPolicy();这段代码是其核心。该方法位于DefaultRetryPolicy这个类中。

该方法如下:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.    public void retry(VolleyError error) throws VolleyError {  
  3.        mCurrentRetryCount++;  
  4.        mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);  
  5.        if (!hasAttemptRemaining()) {  
  6.            throw error;  
  7.        }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. protected boolean hasAttemptRemaining() {  
  2.         return mCurrentRetryCount <= mMaxNumRetries;  
  3.     }  

可以看到,每次调用这个方法,mCurrentRetryCount就会加一,直到hasAttemptRemaining()方法返回false,就是说重试的次数已经够了,就会抛出VolleyError异常,这时while死循环就会退出。


另外,volley还会根据返回的statusCode,抛出不同的的Error。

代码如下:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. if (statusCode < 200 || statusCode > 299) {  
  2.                     throw new IOException();  
  3.                 }  

在上面的performRequest()方法中会catch抛出的IOException,并作出不同的处理。

处理如下:

1、当返回的结果httpResponse为空,则抛出NoConnectionError的异常。

2、当返回结果的内容responseContents为空时,则抛出NetworkError的异常。

3、当返回的结果的内容responseContents不为空时,当statusCode == HttpStatus.SC_UNAUTHORIZED ||statusCode ==HttpStatus.SC_FORBIDDEN时,则会调用attemptRetryOnException()方法进行重试;当statusCode ==HttpStatus.SC_MOVED_PERMANENTLY ||statusCode == HttpStatus.SC_MOVED_TEMPORARILY时,同样调用attemptRetryOnException()方法进行重试;否则会抛出ServerError的异常。


最后,然后我们需要对异常进行捕获,并分发到UI线程进行处理,这就是我们客户端的ErrorListener的回调处理。

在NetworkDispatcher中的run()函数中会会捕获该异常,然后进行分发。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. try{  
  2. .......  
  3. }  
  4. catch (VolleyError volleyError) {  
  5.                 parseAndDeliverNetworkError(request, volleyError);  
  6.             } catch (Exception e) {  
  7.                 VolleyLog.e(e, "Unhandled exception %s", e.toString());  
  8.                 mDelivery.postError(request, new VolleyError(e));  
  9.             }  

这个过程就是进行网络数据请求时,重试机制和错误处理机制的过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值