Skip to content

Commit

Permalink
Provide a ScheduledExecutorService to AsyncRequestQueue/AsyncNetwork.
Browse files Browse the repository at this point in the history
This is a subset of #363 which ensures a ScheduledExecutorService is available,
but does not yet define a new RetryPolicy interface. This gives us the
flexibility to introduce timer-based operations later without needing to change
the ExecutorFactory interface (which is now an abstract class to allow for future
additions, should any others be necessary).
  • Loading branch information
jpd236 committed Sep 26, 2020
1 parent 7b0a311 commit d31a053
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 12 deletions.
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.ScheduledExecutorService;
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 ScheduledExecutorService mNonBlockingScheduledExecutor;

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 non-blocking
* tasks to be scheduled. This method must be called before performing any requests.
*/
@RestrictTo({RestrictTo.Scope.LIBRARY_GROUP})
public void setNonBlockingScheduledExecutor(ScheduledExecutorService executor) {
mNonBlockingScheduledExecutor = 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 non-blocking tasks that need to be scheduled. */
protected ScheduledExecutorService getNonBlockingScheduledExecutor() {
return mNonBlockingScheduledExecutor;
}

/** Gets the {@link AsyncHttpStack} to be used by the network. */
protected AsyncHttpStack getHttpStack() {
return mAsyncStack;
Expand Down
47 changes: 35 additions & 12 deletions src/main/java/com/android/volley/AsyncRequestQueue.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -59,6 +61,9 @@ public class AsyncRequestQueue extends RequestQueue {
/** Executor for non-blocking tasks. */
private ExecutorService mNonBlockingExecutor;

/** Executor to be used for non-blocking tasks that need to be scheduled. */
private ScheduledExecutorService mNonBlockingScheduledExecutor;

/**
* Executor for blocking tasks.
*
Expand Down Expand Up @@ -110,8 +115,10 @@ public void start() {
// Create blocking / non-blocking executors and set them in the network and stack.
mNonBlockingExecutor = mExecutorFactory.createNonBlockingExecutor(getBlockingQueue());
mBlockingExecutor = mExecutorFactory.createBlockingExecutor(getBlockingQueue());
mNonBlockingScheduledExecutor = mExecutorFactory.createNonBlockingScheduledExecutor();
mNetwork.setBlockingExecutor(mBlockingExecutor);
mNetwork.setNonBlockingExecutor(mNonBlockingExecutor);
mNetwork.setNonBlockingScheduledExecutor(mNonBlockingScheduledExecutor);

mNonBlockingExecutor.execute(
new Runnable() {
Expand Down Expand Up @@ -154,6 +161,10 @@ public void stop() {
mBlockingExecutor.shutdownNow();
mBlockingExecutor = null;
}
if (mNonBlockingScheduledExecutor != null) {
mNonBlockingScheduledExecutor.shutdownNow();
mNonBlockingScheduledExecutor = null;
}
}

/** Begins the request by sending it to the Cache or Network. */
Expand Down Expand Up @@ -426,15 +437,17 @@ private void finishRequest(Request<?> mRequest, Response<?> response, boolean ca
}

/**
* This interface may be used by advanced applications to provide custom executors according to
* This class 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
* providing them directly so that Volley can provide a PriorityQueue which will prioritize
* requests according to Request#getPriority.
*/
public interface ExecutorFactory {
ExecutorService createNonBlockingExecutor(BlockingQueue<Runnable> taskQueue);
public static abstract class ExecutorFactory {
abstract ExecutorService createNonBlockingExecutor(BlockingQueue<Runnable> taskQueue);

ExecutorService createBlockingExecutor(BlockingQueue<Runnable> taskQueue);
abstract ExecutorService createBlockingExecutor(BlockingQueue<Runnable> taskQueue);

abstract ScheduledExecutorService createNonBlockingScheduledExecutor();
}

/** Provides a BlockingQueue to be used to create executors. */
Expand Down Expand Up @@ -526,6 +539,12 @@ public ExecutorService createBlockingExecutor(BlockingQueue<Runnable> taskQueue)
taskQueue);
}

@Override
public ScheduledExecutorService createNonBlockingScheduledExecutor() {
return new ScheduledThreadPoolExecutor(
/* corePoolSize= */ 0, getThreadFactory("ScheduledExecutor"));
}

private ThreadPoolExecutor getNewThreadPoolExecutor(
int maximumPoolSize,
final String threadNameSuffix,
Expand All @@ -536,14 +555,18 @@ private ThreadPoolExecutor getNewThreadPoolExecutor(
/* keepAliveTime= */ 60,
/* unit= */ TimeUnit.SECONDS,
taskQueue,
new ThreadFactory() {
@Override
public Thread newThread(@NonNull Runnable runnable) {
Thread t = Executors.defaultThreadFactory().newThread(runnable);
t.setName("Volley-" + threadNameSuffix);
return t;
}
});
getThreadFactory(threadNameSuffix));
}

private ThreadFactory getThreadFactory(final String threadNameSuffix) {
return new ThreadFactory() {
@Override
public Thread newThread(@NonNull Runnable runnable) {
Thread t = Executors.defaultThreadFactory().newThread(runnable);
t.setName("Volley-" + threadNameSuffix);
return t;
}
};
}
};
}
Expand Down
7 changes: 7 additions & 0 deletions src/test/java/com/android/volley/AsyncRequestQueueTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.google.common.util.concurrent.MoreExecutors;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -43,6 +44,7 @@
public class AsyncRequestQueueTest {

@Mock private AsyncNetwork mMockNetwork;
@Mock private ScheduledExecutorService mMockScheduledExecutor;
private AsyncRequestQueue queue;

@Before
Expand All @@ -66,6 +68,11 @@ public ExecutorService createBlockingExecutor(
BlockingQueue<Runnable> taskQueue) {
return MoreExecutors.newDirectExecutorService();
}

@Override
public ScheduledExecutorService createNonBlockingScheduledExecutor() {
return mMockScheduledExecutor;
}
})
.build();
}
Expand Down

0 comments on commit d31a053

Please sign in to comment.