当我们进行网络请求的时候,可能会面临很多复杂的环境。比如网络环境不好、服务器异常等。
所以当我们使用网络请求框架进行应用开发的时候,一个好的重试机制,可以让我们设定适当的重试次数,不会只请求一次或者一直重试请求无数次,以应对复杂的网络环境;一个好的错误处理机制可以让我们在出现错误的时候,及时作出反馈,不会一直在等待,使应用有一个友好的用户体验。
volley的重试机制的使用是比较简单。
我们可以通过设置RetryPolicy来设置volley的重试次数和请求超时时间。我们可以在使用该框架的时候这样进行设置:
request.setRetryPolicy(new DefaultRetryPolicy(20 *1000, 1, 1.0f));
可以看到DefaultRetryPolicy这个类的构造函数:
- public DefaultRetryPolicy() {
- this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
- }
其中第一个参数就是设置超时的时间,第二个参数是设置重试的次数,第三个是一个基数时间,用于算两次重试请求之间的时间间隔。但我们没有进行参数的设置的时候,这三个参数都有一个默认的值,其中超时时间是2500ms,重试次数是1次,基数时间是1。
我们在app开发时这样简单的设置,但是,volley中是如何使用的呢,原理分析会在下面展示。
类:BasicNetWork.Java
该类中的performRequest()方法调用HttpStack进行网络请求,并对请求回来的结果进行解析封装,并返回最后的结果。其中重试机制和错误处理机制的使用也是在该方法中进行处理。
方法如下:
- @Override
- public NetworkResponse performRequest(Request<?> request) throws VolleyError {
- long requestStart = SystemClock.elapsedRealtime();
- while (true) {
- HttpResponse httpResponse = null;
- byte[] responseContents = null;
- Map<String, String> responseHeaders = new HashMap<String, String>();
- try {
-
- Map<String, String> headers = new HashMap<String, String>();
- addCacheHeaders(headers, request.getCacheEntry());
- httpResponse = mHttpStack.performRequest(request, headers);
- StatusLine statusLine = httpResponse.getStatusLine();
- int statusCode = statusLine.getStatusCode();
-
- responseHeaders = convertHeaders(httpResponse.getAllHeaders());
-
- if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
- return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
- request.getCacheEntry() == null ? null : request.getCacheEntry().data,
- responseHeaders, true);
- }
-
-
- if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
- String newUrl = responseHeaders.get("Location");
- request.setRedirectUrl(newUrl);
- }
-
-
- if (httpResponse.getEntity() != null) {
- responseContents = entityToBytes(httpResponse.getEntity());
- } else {
-
-
- responseContents = new byte[0];
- }
-
-
- long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
- logSlowRequests(requestLifetime, request, responseContents, statusLine);
-
- if (statusCode < 200 || statusCode > 299) {
- throw new IOException();
- }
- return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
- } catch (SocketTimeoutException e) {
- attemptRetryOnException("socket", request, new TimeoutError());
- } catch (ConnectTimeoutException e) {
- attemptRetryOnException("connection", request, new TimeoutError());
- } catch (MalformedURLException e) {
- throw new RuntimeException("Bad URL " + request.getUrl(), e);
- } catch (IOException e) {
- int statusCode = 0;
- NetworkResponse networkResponse = null;
- if (httpResponse != null) {
- statusCode = httpResponse.getStatusLine().getStatusCode();
- } else {
- throw new NoConnectionError(e);
- }
- if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
- statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
- VolleyLog.e("Request at %s has been redirected to %s", request.getOriginUrl(), request.getUrl());
- } else {
- VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
- }
- if (responseContents != null) {
- networkResponse = new NetworkResponse(statusCode, responseContents,
- responseHeaders, false);
- if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
- statusCode == HttpStatus.SC_FORBIDDEN) {
- attemptRetryOnException("auth",
- request, new AuthFailureError(networkResponse));
- } else if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
- statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
- attemptRetryOnException("redirect",
- request, new AuthFailureError(networkResponse));
- } else {
-
- throw new ServerError(networkResponse);
- }
- } else {
- throw new NetworkError(networkResponse);
- }
- }
- }
- }
可以分析,看到该方法中有一个while(true)的死循环,其实就是为了重试机制的使用(在请求的次数没有达到设置的次数时,会循环进行网络请求的操作)。
httpResponse = mHttpStack.performRequest(request, headers);
这行代码就是真正的进行网络数据请求的代码,并返回得到的数据。然而在请求网络数据的时候,可能会出现异常,然后出现异常是就需要对异常进行处理。 这段代码会抓取SocketTimeoutException、ConnectTimeoutException、MalformedURLException、IOException这几个异常,其中在SocketTimeoutException和ConnectTimeoutException会进行重试的机制调用,就是调用attemptRetryOnException()这个方法。
方法如下:
- private static void attemptRetryOnException(String logPrefix, Request<?> request,
- VolleyError exception) throws VolleyError {
- RetryPolicy retryPolicy = request.getRetryPolicy();
- int oldTimeout = request.getTimeoutMs();
-
- try {
- retryPolicy.retry(exception);
- } catch (VolleyError e) {
- request.addMarker(
- String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
- throw e;
- }
- request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
- }
其中RetryPolicy
retryPolicy
=
request
.getRetryPolicy();这段代码是其核心。该方法位于DefaultRetryPolicy这个类中。
该方法如下:
- @Override
- public void retry(VolleyError error) throws VolleyError {
- mCurrentRetryCount++;
- mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
- if (!hasAttemptRemaining()) {
- throw error;
- }
- protected boolean hasAttemptRemaining() {
- return mCurrentRetryCount <= mMaxNumRetries;
- }
可以看到,每次调用这个方法,mCurrentRetryCount就会加一,直到hasAttemptRemaining()方法返回false,就是说重试的次数已经够了,就会抛出VolleyError异常,这时while死循环就会退出。
另外,volley还会根据返回的statusCode,抛出不同的的Error。
代码如下:
- if (statusCode < 200 || statusCode > 299) {
- throw new IOException();
- }
在上面的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()函数中会会捕获该异常,然后进行分发。
- try{
- .......
- }
- catch (VolleyError volleyError) {
- parseAndDeliverNetworkError(request, volleyError);
- } catch (Exception e) {
- VolleyLog.e(e, "Unhandled exception %s", e.toString());
- mDelivery.postError(request, new VolleyError(e));
- }
这个过程就是进行网络数据请求时,重试机制和错误处理机制的过程。