Volley 소스 다시 보기 (2): 정책 다시 시도
1. 핵심 클래스 2. 재시험 전략 참고 자료
본고는 Volley 프레임워크의 작업 절차에 대해 어느 정도 알고 있는 상황에서 계속 깊이 있게 연구하고자 합니다. 여기에 제가 쓴 윗글인 을 붙여 드리겠습니다.
핵심류
RetryPolicy: Volley 정의된 정책 재시도 요청 인터페이스
public interface RetryPolicy {
/**
* Returns the current timeout (used for logging).
*/
public int getCurrentTimeout();
/**
* Returns the current retry count (used for logging).
*/
public int getCurrentRetryCount();
/**
* Prepares for the next retry by applying a backoff to the timeout.
* @param error The error code of the last attempt.
* @throws VolleyError In the event that the retry could not be performed (for example if we
* ran out of attempts), the passed in error is thrown.
*/
public void retry(VolleyError error) throws VolleyError;
}
DefaultRetryPolicy:RetryPolicy의 실현 하위 클래스
public class DefaultRetryPolicy implements RetryPolicy {
/** The current timeout in milliseconds. */
private int mCurrentTimeoutMs;
/** The current retry count. */
private int mCurrentRetryCount;
/** The maximum number of attempts. */
private final int mMaxNumRetries;
/** The backoff multiplier for the policy. */
private final float mBackoffMultiplier;
/** The default socket timeout in milliseconds */
public static final int DEFAULT_TIMEOUT_MS = 2500;
/** The default number of retries */
public static final int DEFAULT_MAX_RETRIES = 0;
/** The default backoff multiplier */
/**
* 2.5s
* DEFAULT_BACKOFF_MULT = 1f, HttpUrlConnection 2.5s*1f*mCurrentRetryCount
* DEFAULT_BACKOFF_MULT = 2f, :2.5s+2.5s*2=7.5s, :7.5s+7.5s*2=22.5s
*/
public static final float DEFAULT_BACKOFF_MULT = 1f;
/**
* Constructs a new retry policy using the default timeouts.
*/
public DefaultRetryPolicy() {
this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
}
/**
* Constructs a new retry policy.
* @param initialTimeoutMs The initial timeout for the policy.
* @param maxNumRetries The maximum number of retries.
* @param backoffMultiplier Backoff multiplier for the policy.
*/
public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
mCurrentTimeoutMs = initialTimeoutMs;
mMaxNumRetries = maxNumRetries;
mBackoffMultiplier = backoffMultiplier;
}
/**
* Returns the current timeout.
*/
@Override
public int getCurrentTimeout() {
return mCurrentTimeoutMs;
}
/**
* Returns the current retry count.
*/
@Override
public int getCurrentRetryCount() {
return mCurrentRetryCount;
}
/**
* Returns the backoff multiplier for the policy.
*/
public float getBackoffMultiplier() {
return mBackoffMultiplier;
}
/**
* Prepares for the next retry by applying a backoff to the timeout.
* @param error The error code of the last attempt.
*/
@Override
public void retry(VolleyError error) throws VolleyError {
// ++
mCurrentRetryCount++;
//
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
// , VolleyError
if (!hasAttemptRemaining()) {
throw error;
}
}
/**
* Request
* Returns true if this policy has attempts remaining, false otherwise.
*/
protected boolean hasAttemptRemaining() {
return mCurrentRetryCount <= mMaxNumRetries;
}
}
2. 정책 재시도
원본 코드를 깊이 파고들어 읽으면 Retry 방법이 Volley Error의 이상을 던질 수 있지만 이 방법 내부에서 네트워크 요청을 다시 시작하는 것이 아니라 재시도 정책의 속성을 변경합니다. 예를 들어 시간 초과와 재시도 횟수는 재시도 정책 설정의 제한을 초과하면 달라집니다. 이것은Default Retry Policy에서 검증할 수 있습니다.그렇다면 어떻게 다시 시도할 수 있을까. 우리는 원본 리트리 방법이 호출된 곳을 추적할 수 있고Basic Network의attempt Retry On Exception 방법에 도착했다.
public class BasicNetwork implements Network {
......
/**
* ,
*/
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));
// , catch , catch , , while(true)
throw e;
}
// ,
request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
}
}
attempt Retry On Exception 방법이 호출된 곳을 계속 추적하여 Basic Network의perform Request 방법에 도착했습니다.
public class BasicNetwork implements Network {
......
@Override
public NetworkResponse performRequest(Request> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
......
try {
......
// 、 , , 。 attemptRetryOnException
// catch return, while(true) , , , 、
return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException e) {
//1.
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
//2.
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, SystemClock.elapsedRealtime() - requestStart);
if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
statusCode == HttpStatus.SC_FORBIDDEN) {
//3.
attemptRetryOnException("auth",
request, new AuthFailureError(networkResponse));
} else if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
//4.
attemptRetryOnException("redirect",
request, new RedirectError(networkResponse));
} else {
// TODO: Only throw ServerError for 5xx status codes.
throw new ServerError(networkResponse);
}
} else {
throw new NetworkError(e);
}
}
}
}
}
performRequest 이 방법명은 낯익습니까?네, 이것은 제가 지난 글에서 간단하게 언급한 적이 있습니다. NetworkDispatcher의run 방법에서 mNetWork 대상 (즉 BasicNetwork) 은perform Request를 호출하여 요청을 실행하고, 이 방법 내부에는while (true) 순환이 있는 것을 기억합니다.
이 방법에서 attempt Retry On Exception은 모두 네 번 호출되었다.
위에서 말한 것은 도대체 어떻게 처리하고while 순환을 종료하는지 의문이 있을 수 있습니다.Basic Network의perform Request 방법은 Volley Error 이상을 포착하지 않았기 때문에try &catch에 살지 않은 이상은 계속 밖으로 던져집니다. 여기서 Network Dispatcher의run 방법에 대해 돌아봅시다.
public class NetworkDispatcher extends Thread {
......
@Override
public void run() {
......
while (true) {
......
try {
......
// Perform the network request. ,BasicNetwork VolleyError
NetworkResponse networkResponse = mNetwork.performRequest(request);
......
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
// VolleyError , Handler ErrorListener onErrorResponse
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(request, volleyError);
}
}
}
}
Request에서 다시 시도할 수 없는 Volley Error 이상은NetworkDispatcher에 의해 포착되고 Delivery를 이용하여 사용자가 설정한 Error Listener를 리셋합니다.
참고 자료
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.