diff --git a/src/main/java/com/android/volley/AsyncNetwork.java b/src/main/java/com/android/volley/AsyncNetwork.java index f9cdd589..d9d5f0ff 100644 --- a/src/main/java/com/android/volley/AsyncNetwork.java +++ b/src/main/java/com/android/volley/AsyncNetwork.java @@ -20,6 +20,7 @@ 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. */ @@ -27,6 +28,7 @@ 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; @@ -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; @@ -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; diff --git a/src/main/java/com/android/volley/AsyncRequestQueue.java b/src/main/java/com/android/volley/AsyncRequestQueue.java index bbbcf927..f4991deb 100644 --- a/src/main/java/com/android/volley/AsyncRequestQueue.java +++ b/src/main/java/com/android/volley/AsyncRequestQueue.java @@ -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; @@ -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. * @@ -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() { @@ -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. */ @@ -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 taskQueue); + public static abstract class ExecutorFactory { + abstract ExecutorService createNonBlockingExecutor(BlockingQueue taskQueue); - ExecutorService createBlockingExecutor(BlockingQueue taskQueue); + abstract ExecutorService createBlockingExecutor(BlockingQueue taskQueue); + + abstract ScheduledExecutorService createNonBlockingScheduledExecutor(); } /** Provides a BlockingQueue to be used to create executors. */ @@ -526,6 +539,12 @@ public ExecutorService createBlockingExecutor(BlockingQueue taskQueue) taskQueue); } + @Override + public ScheduledExecutorService createNonBlockingScheduledExecutor() { + return new ScheduledThreadPoolExecutor( + /* corePoolSize= */ 0, getThreadFactory("ScheduledExecutor")); + } + private ThreadPoolExecutor getNewThreadPoolExecutor( int maximumPoolSize, final String threadNameSuffix, @@ -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; + } + }; } }; } diff --git a/src/test/java/com/android/volley/AsyncRequestQueueTest.java b/src/test/java/com/android/volley/AsyncRequestQueueTest.java index 05ecf133..36936e26 100644 --- a/src/test/java/com/android/volley/AsyncRequestQueueTest.java +++ b/src/test/java/com/android/volley/AsyncRequestQueueTest.java @@ -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; @@ -43,6 +44,7 @@ public class AsyncRequestQueueTest { @Mock private AsyncNetwork mMockNetwork; + @Mock private ScheduledExecutorService mMockScheduledExecutor; private AsyncRequestQueue queue; @Before @@ -66,6 +68,11 @@ public ExecutorService createBlockingExecutor( BlockingQueue taskQueue) { return MoreExecutors.newDirectExecutorService(); } + + @Override + public ScheduledExecutorService createNonBlockingScheduledExecutor() { + return mMockScheduledExecutor; + } }) .build(); }