Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New retry api for supporting timeouts before retrying again #363

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/main/java/com/android/volley/AsyncNetwork.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
import com.android.volley.toolbox.AsyncHttpStack;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicReference;

/** An asynchronous implementation of {@link Network} to perform requests. */
public abstract class AsyncNetwork implements Network {
private final AsyncHttpStack mAsyncStack;
private ExecutorService mBlockingExecutor;
private ExecutorService mNonBlockingExecutor;
private ScheduledThreadPoolExecutor mScheduledExecutor;

protected AsyncNetwork(AsyncHttpStack stack) {
mAsyncStack = stack;
Expand Down Expand Up @@ -113,6 +115,15 @@ public void setBlockingExecutor(ExecutorService executor) {
mAsyncStack.setBlockingExecutor(executor);
}

/**
* This method sets the scheduled executor to be used by the network and stack for tasks to be
* scheduled. This method must be called before performing any requests.
*/
@RestrictTo({RestrictTo.Scope.LIBRARY_GROUP})
public void setScheduledExecutor(ScheduledThreadPoolExecutor executor) {
mScheduledExecutor = executor;
}

/** Gets blocking executor to perform any potentially blocking tasks. */
protected ExecutorService getBlockingExecutor() {
return mBlockingExecutor;
Expand All @@ -123,6 +134,11 @@ protected ExecutorService getNonBlockingExecutor() {
return mNonBlockingExecutor;
}

/** Gets scheduled executor to perform any tasks that need to be scheduled. */
protected ScheduledThreadPoolExecutor getScheduledExecutor() {
return mScheduledExecutor;
}

/** Gets the {@link AsyncHttpStack} to be used by the network. */
protected AsyncHttpStack getHttpStack() {
return mAsyncStack;
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/com/android/volley/AsyncRequestQueue.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -67,6 +68,19 @@ public class AsyncRequestQueue extends RequestQueue {
*/
private ExecutorService mBlockingExecutor;

/** Executor to be used for tasks that need to be scheduled. */
private ScheduledThreadPoolExecutor mScheduledExecutor =
new ScheduledThreadPoolExecutor(
/* corePoolSize= */ 0,
new ThreadFactory() {
@Override
public Thread newThread(@NonNull Runnable runnable) {
Thread t = Executors.defaultThreadFactory().newThread(runnable);
t.setName("Volley-ScheduledExecutor");
return t;
}
});

/**
* This interface may be used by advanced applications to provide custom executors according to
* their needs. Apps must create ExecutorServices dynamically given a blocking queue rather than
Expand Down Expand Up @@ -112,6 +126,7 @@ public void start() {
mBlockingExecutor = mExecutorFactory.createBlockingExecutor(getBlockingQueue());
mNetwork.setBlockingExecutor(mBlockingExecutor);
mNetwork.setNonBlockingExecutor(mNonBlockingExecutor);
mNetwork.setScheduledExecutor(mScheduledExecutor);

mNonBlockingExecutor.execute(
new Runnable() {
Expand Down
43 changes: 43 additions & 0 deletions src/main/java/com/android/volley/DefaultTimeoutRetryPolicy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.android.volley;

/** Default retry policy for requests that want a timeout before retrying an error. */
public class DefaultTimeoutRetryPolicy extends DefaultRetryPolicy implements TimeoutRetryPolicy {
/** The timeout before retrying a failed request in milliseconds. */
int mTimeoutBeforeRetryMs;

/**
* Constructs a new retry policy
*
* @param timeoutBeforeRetryMs is the timeout before retrying a failed request in milliseconds.
*/
public DefaultTimeoutRetryPolicy(int timeoutBeforeRetryMs) {
super();
if (timeoutBeforeRetryMs < 0) {
throw new IllegalArgumentException("timeout cannot be negative");
}
mTimeoutBeforeRetryMs = timeoutBeforeRetryMs;
}

/**
* 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.
* @param timeoutBeforeRetryMs Timeout before retrying a failed request in milliseconds.
*/
public DefaultTimeoutRetryPolicy(
int initialTimeoutMs,
int maxNumRetries,
float backoffMultiplier,
int timeoutBeforeRetryMs) {
super(initialTimeoutMs, maxNumRetries, backoffMultiplier);
mTimeoutBeforeRetryMs = timeoutBeforeRetryMs;
}

/** Returns the timeout before attempting to retry the request in ms. */
@Override
public int getTimeoutBeforeRetryMs() {
return mTimeoutBeforeRetryMs;
}
}
12 changes: 12 additions & 0 deletions src/main/java/com/android/volley/TimeoutRetryPolicy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.android.volley;

/**
* Retry policy for a request.
*
* <p>Same as {@link RetryPolicy}, except instead of triggering the retry attempt instantly, the
* user can set a timeout in milliseconds to wait before retrying the request.
*/
public interface TimeoutRetryPolicy extends RetryPolicy {
/** Returns the timeout before attempting to retry the request in ms. */
int getTimeoutBeforeRetryMs();
}
29 changes: 26 additions & 3 deletions src/main/java/com/android/volley/toolbox/BasicAsyncNetwork.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.RequestTask;
import com.android.volley.RetryPolicy;
import com.android.volley.TimeoutRetryPolicy;
import com.android.volley.VolleyError;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/** A network performing Volley requests over an {@link HttpStack}. */
public class BasicAsyncNetwork extends AsyncNetwork {
Expand Down Expand Up @@ -102,8 +106,8 @@ private void onRequestSucceeded(

/* Method to be called after a failed network request */
private void onRequestFailed(
Request<?> request,
OnRequestComplete callback,
final Request<?> request,
final OnRequestComplete callback,
IOException exception,
long requestStartMs,
@Nullable HttpResponse httpResponse,
Expand All @@ -115,7 +119,26 @@ private void onRequestFailed(
callback.onError(volleyError);
return;
}
performRequest(request, callback);

RetryPolicy policy = request.getRetryPolicy();
// If the retry policy is not an instance of TimeoutRetryPolicy OR no scheduling executor
// was set, then just perform the request immediately.
if (!(policy instanceof TimeoutRetryPolicy) || getScheduledExecutor() == null) {
performRequest(request, callback);
} else {
// Otherwise. schedule the request on the ScheduledExecutor for the appropriate time.
ScheduledFuture<?> retry =
getScheduledExecutor()
.schedule(
new Runnable() {
@Override
public void run() {
performRequest(request, callback);
}
},
((TimeoutRetryPolicy) policy).getTimeoutBeforeRetryMs(),
TimeUnit.MILLISECONDS);
}
}

@Override
Expand Down