[关闭]
@946898963 2018-05-18T11:25:11.000000Z 字数 5773 阅读 1229

Volley的请求重试策略相关源码分析

Android控件跟框架 Android源码分析


请求重试策略的相关接口和类:RetryPolicy和DefaultRetryPolicy

RetryPolicy的源码:

  1. /**
  2. * 请求的重新请求策略
  3. */
  4. public interface RetryPolicy {
  5. /**
  6. * 获取当前请求用时(用于Log)
  7. */
  8. public int getCurrentTimeout();
  9. /**
  10. * 已经重新请求了几次
  11. */
  12. public int getCurrentRetryCount();
  13. /**
  14. * 确定是否重试,参数为这次异常的具体信息。在请求异常时此接口会被调用,可在此函数实现中抛出传入的异常表示停止重试。
  15. */
  16. public void retry(VolleyError error) throws VolleyError;
  17. }

接口定义了三个方法:获取当前超时时间、获取重试次数和重试。最重要的是第三个方法

  1. public void retry(VolleyError error) throws VolleyError

第三个方法会抛出异常,也就是说在retry内部会抛出异常,在retry内部的操作是变更重试策略的属性,如超时时间和重试次数,当超过了重试策略设定的限定就会抛出异常。需要注意的是:retry并不是真正的去重新发出网络请求。请求的重试是在BasicNetwork内实现的。

DefaultRetryPolicy,RetryPolicy的默认实现类,也是volley的默认的请求重试策略。
DefaultRetryPolicy的源码:

  1. public class DefaultRetryPolicy implements RetryPolicy {
  2. private int mCurrentTimeoutMs;
  3. private int mCurrentRetryCount;
  4. private final int mMaxNumRetries;
  5. private final float mBackoffMultiplier;
  6. public static final int DEFAULT_TIMEOUT_MS = 2500;
  7. public static final int DEFAULT_MAX_RETRIES = 1;
  8. public static final float DEFAULT_BACKOFF_MULT = 1f;
  9. public DefaultRetryPolicy() {
  10. this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
  11. }
  12. public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
  13. mCurrentTimeoutMs = initialTimeoutMs;
  14. mMaxNumRetries = maxNumRetries;
  15. mBackoffMultiplier = backoffMultiplier;
  16. }
  17. @Override
  18. public int getCurrentTimeout() {
  19. return mCurrentTimeoutMs;
  20. }
  21. @Override
  22. public int getCurrentRetryCount() {
  23. return mCurrentRetryCount;
  24. }
  25. public float getBackoffMultiplier() {
  26. return mBackoffMultiplier;
  27. }
  28. @Override
  29. public void retry(VolleyError error) throws VolleyError {
  30. mCurrentRetryCount++;
  31. mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
  32. if (!hasAttemptRemaining()) {
  33. throw error;
  34. }
  35. }
  36. protected boolean hasAttemptRemaining() {
  37. return mCurrentRetryCount <= mMaxNumRetries;
  38. }
  39. }

该类内部定义了重试策略的必备属性:

  1. /**当前超时时间*/
  2. private int mCurrentTimeoutMs;
  3. /**当前重试次数*/
  4. private int mCurrentRetryCount;
  5. /**最大重试次数*/
  6. private final int mMaxNumRetries;
  7. /**表示每次重试之前的 timeout 该乘以的因子,每重试一次,超时时间就变化一次*/
  8. private final float mBackoffMultiplier;
  9. /**默认超时时间*/
  10. public static final int DEFAULT_TIMEOUT_MS = 5000;
  11. /**默认最大重试次数*/
  12. public static final int DEFAULT_MAX_RETRIES = 1;

DefaultRetryPolicy有两个构造方法,在创建实例时使用了默认的属性。

  1. /**
  2. * Constructs a new retry policy using the default timeouts.
  3. * 构造时,使用了默认的几个参数
  4. */
  5. public DefaultRetryPolicy() {
  6. this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
  7. }
  8. /**
  9. * Constructs a new retry policy.
  10. * @param initialTimeoutMs 当前超时时间
  11. * @param maxNumRetries 最大重试次数
  12. * @param backoffMultiplier Backoff multiplier for the policy.
  13. */
  14. public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
  15. mCurrentTimeoutMs = initialTimeoutMs;
  16. mMaxNumRetries = maxNumRetries;
  17. mBackoffMultiplier = backoffMultiplier;
  18. }

再看另外两个方法:

  1. @Override
  2. public void retry(VolleyError error) throws VolleyError {
  3. //重试次数+1
  4. mCurrentRetryCount++;
  5. //超时时间每次都变化,增加
  6. mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
  7. if (!hasAttemptRemaining()) {
  8. //如果超过了最大重试次数就抛出异常
  9. throw error;
  10. }
  11. }
  12. /**
  13. * 当前重试次数是否已经达到最大重试次数
  14. */
  15. protected boolean hasAttemptRemaining() {
  16. return mCurrentRetryCount <= mMaxNumRetries;
  17. }

hasAttemptRemaining()方法返回的是,当前请求次数是否已经超过了最大的重试次数。retry()方法则是定义重试请求后属性的变化,如果超过了最大次数那么抛出异常。

真正的网络请求的重试是在BasicNetwork内实现的,现在看下BasicNetwork中负责网络请求的performRequest方法的源码:

  1. /**
  2. * 执行具体的网络请求 ,需要Volley的Request,返回的是可以被传递的响应
  3. */
  4. @Override
  5. public NetworkResponse performRequest(Request<?> request) throws VolleyError {
  6. long requestStart = SystemClock.elapsedRealtime();
  7. //这里使用while(true)的含义是:保证请求重试策略的执行。
  8. //如果网络正常返回结果 那么直接return
  9. //如果需要进行请求重试,就用到这里了,保证了可以进行请求重试
  10. while (true) {
  11. ......
  12. try {
  13. ...................
  14. return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
  15. //如果发生超时,认证失败等错误,进行重试操作,直到成功、抛出异常(不满足重试策略等)结束
  16. //当catch后没有执行上边的return 而当前又是一个while(true)循环,可以保证下面的请求重试的执行,是利用循环进行请求重试,请求重试策略只是记录重试的次数、超时 时间等内容。
  17. } catch (SocketTimeoutException e) {
  18. //当出现异常的时候,尝试进行请求重试
  19. attemptRetryOnException("socket", request, new TimeoutError());</span>
  20. } catch (ConnectTimeoutException e) {
  21. //当出现异常的时候,尝试进行请求重试
  22. attemptRetryOnException("connection", request, new TimeoutError());
  23. } catch (MalformedURLException e) {
  24. //url不正常异常
  25. throw new RuntimeException("Bad URL " + request.getUrl(), e);
  26. } catch (IOException e) {
  27. //当出现IO异常时,在try内读取数据体时,如果出现IO异常,那么捕获异常,继续完成创建NetworkResponse的过程
  28. int statusCode = 0;
  29. NetworkResponse networkResponse = null;
  30. //如果响应不为空
  31. if (httpResponse != null) {
  32. //获取返回的状态码
  33. statusCode = httpResponse.getStatusLine().getStatusCode();
  34. } else {
  35. //响应为空就表明 网络连接错误
  36. throw new NoConnectionError(e);
  37. }
  38. VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
  39. if (responseContents != null) {
  40. //根据状态码、响应的实体数、响应头信息创建可被传递的响应
  41. networkResponse = new NetworkResponse(statusCode, responseContents, responseHeaders, false);
  42. //如果状态码是授权未通过
  43. if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
  44. statusCode == HttpStatus.SC_FORBIDDEN) {
  45. //请求重试策略
  46. attemptRetryOnException("auth",
  47. request, new AuthFailureError(networkResponse));
  48. } else {
  49. // TODO: Only throw ServerError for 5xx status codes.
  50. throw new ServerError(networkResponse);
  51. }
  52. } else {
  53. throw new NetworkError(networkResponse);
  54. }
  55. }
  56. }
  57. }

performRequest(Requestrequest)方法是执行网络请求的方法,请求重试也是在这里进行。在方法的内部是用while(true)括起来的,也就是说如果该方法正常执行完毕或者抛出异常时,必然就跳出循环了,但是如果请求失败没有return并且在catch内也没有超过重试策略限定条件时,必然会while(true)下重新请求一次,这样就达到了重试的目的。

attemptRetryOnException()方法的源码:

  1. /**
  2. * 请求重试策略
  3. */
  4. private static void attemptRetryOnException(String logPrefix, Request<?> request,
  5. VolleyError exception) throws VolleyError {
  6. //获得该请求的请求重试策略
  7. RetryPolicy retryPolicy = request.getRetryPolicy();
  8. //请求重试策略的超时时间
  9. int oldTimeout = request.getTimeoutMs();
  10. try {
  11. //内部实现,重试次数+1 超时时间变化
  12. //如果重试次数超过限定的最大次数,该方法抛出异常
  13. retryPolicy.retry(exception);
  14. } catch (VolleyError e) {
  15. //当超过了最大重试次数,捕获到异常,给改请求添加标记 标记超时
  16. request.addMarker(
  17. String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
  18. //这里才是最重要的,当仍然可以进行重试的时候,不会执行到catch语句,但是当执行到catch语句的时候,表示已经不能进行重试了,就抛出异常 这样while(true)循环就断了
  19. throw e;
  20. }
  21. //给请求添加标记,请求了多少次
  22. request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
  23. }

DefaultRetryPolicy是请求重试的策略,它规定了超时时间、超时时间的变化、请求重试次数、最大请求重试次数以及请求重试后的变化,但是DefaultRetryPolicy并没有去执行真正的网络重试请求,仅仅是规划了,真正的重试还是要到网络请求类中。

参考链接:

Android中的volley_12_请求重试策略RetryPolicy和DefaultRetryPolicy(作者的其他的文章也值得阅读)

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注