Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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.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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not keeping the interface?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is taken from the Android API guidelines - since we can't use default interface methods in Volley, abstract classes are preferable because it gives us the flexibility to add methods in later versions. Since it seems like there's clearly value in such flexibility (as we're adding a scheduled Executor in this very PR), it seemed like a good time to make it an abstract class as well. I can't think of a concrete example of something we'd be likely to add here now that the scheduled executor is here, but I also don't see much benefit to keeping this an interface since I can't imagine anybody needs multiple inheritance to implement it.

ExecutorService createNonBlockingExecutor(BlockingQueue<Runnable> taskQueue);
public abstract static 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
8 changes: 8 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,12 @@ public ExecutorService createBlockingExecutor(
BlockingQueue<Runnable> taskQueue) {
return MoreExecutors.newDirectExecutorService();
}

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