The directory tree of the source code: - [android]
- ┗━[volley]
- ┣━AuthFailureError.java
- ┣━Cache.java
- ┣━CacheDispatcher.java
- ┣━DefaultRetryPolicy.java
- ┣━ExecutorDelivery.java
- ┣━InternalUtils.java
- ┣━Network.java
- ┣━NetworkDispatcher.java
- ┣━NetworkError.java
- ┣━NetworkResponse.java
- ┣━NoConnectionError.java
- ┣━ParseError.java
- ┣━RedirectError.java
- ┣━Request.java
- ┣━RequestQueue.java
- ┣━Response.java
- ┣━ResponseDelivery.java
- ┣━RetryPolicy.java
- ┣━ServerError.java
- ┣━TimeoutError.java
- ┣━[toolbox]
- ┃ ┣━AndroidAuthenticator.java
- ┃ ┣━Authenticator.java
- ┃ ┣━BasicNetwork.java
- ┃ ┣━ByteArrayPool.java
- ┃ ┣━ClearCacheRequest.java
- ┃ ┣━DiskBasedCache.java
- ┃ ┣━HttpClientStack.java
- ┃ ┣━HttpHeaderParser.java
- ┃ ┣━HttpStack.java
- ┃ ┣━HurlStack.java
- ┃ ┣━ImageLoader.java
- ┃ ┣━ImageRequest.java
- ┃ ┣━JsonArrayRequest.java
- ┃ ┣━JsonObjectRequest.java
- ┃ ┣━JsonRequest.java
- ┃ ┣━NetworkImageView.java
- ┃ ┣━NoCache.java
- ┃ ┣━PoolingByteArrayOutputStream.java
- ┃ ┣━RequestFuture.java
- ┃ ┣━StringRequest.java
- ┃ ┗━Volley.java
- ┣━VolleyError.java
- ┗━VolleyLog.java
It can be seen that the Volley source code is placed in a rather messy way, and the classes of different functional modules are not classified into different packages. In contrast, the source code structure of UIL is more standardized and reasonable. Start with common cases and infer the project architecture The simplest usage example given on the official website is as follows: final TextView mTextView = (TextView) findViewById(R.id.text); -
- RequestQueue queue = Volley.newRequestQueue( this );
- String url = "http://www.google.com" ;
-
-
- StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
- new Response.Listener<String>() {
- @Override
- public void onResponse(String response) {
-
- mTextView.setText( "Response is: " + response.substring( 0 , 500 ));
- }
- }, new Response.ErrorListener() {
- @Override
- public void onErrorResponse(VolleyError error) {
- mTextView.setText( "That didn't work!" );
- }
- });
-
- queue.add(stringRequest);
-
- Combined with the following picture:
-
- Architecture diagram
-
- We can get a general idea of how to use Volley (see comments) and its internal structure. The following is a brief source code level description of this use case.
- Volley Class
-
- The Volley class provides four static methods to facilitate users to create new queues. Among them:
-
- public static RequestQueue newRequestQueue(Context context) {
- return newRequestQueue(context, null );
- }
-
- One sentence will eventually call:
-
-
- public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
- File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
-
- String userAgent = "volley/0" ;
- try {
- String packageName = context.getPackageName();
- PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0 );
- userAgent = packageName + "/" + info.versionCode;
- } catch (NameNotFoundException e) {
- }
-
- if (stack == null ) {
- if (Build.VERSION.SDK_INT >= 9 ) {
- stack = new HurlStack();
- } else {
-
-
- stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
- }
- }
-
- Network network = new BasicNetwork(stack);
-
- RequestQueue queue;
- if (maxDiskCacheBytes <= - 1 )
- {
-
- queue = new RequestQueue( new DiskBasedCache(cacheDir), network);
- }
- else
- {
-
- queue = new RequestQueue( new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
- }
-
- queue.start();
-
- return queue;
- }
Worth noting: Volley will decide whether to use java.net.HttpURLConnection (Build.VERSION.SDK_INT >= 9) or org.apache.http.client.HttpClient based on the SDK version. After creating a new Queue, the Queue will be started immediately. - The stack class is responsible for sending the request (com.android.volley.Request) and getting the response (org.apache.http.HttpResponse), and the network class is responsible for analyzing and processing the response and packaging it into a NetworkResponse (com.android.volley.NetworkResponse).
-
- We first ignore the network-related details and look at the queue implementation and request scheduling strategy.
- RequestQueue
-
- Let's first look at the construction method of RequestQueue:
-
- public RequestQueue(Cache cache, Network network) {
- this (cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
- }
Call: - public RequestQueue(Cache cache, Network network, int threadPoolSize) {
- this (cache, network, threadPoolSize,
- new ExecutorDelivery( new Handler(Looper.getMainLooper())));
- }
-
- Here comes a new face ExecutorDelivery. According to the literal meaning, it is responsible for distributing the request results to the main thread, or executing the callback (listener) on the main thread. Continue calling:
-
- public RequestQueue(Cache cache, Network network, int threadPoolSize,
- ResponseDelivery delivery) {
- mCache = cache;
- mNetwork = network;
- mDispatchers = new NetworkDispatcher[threadPoolSize];
- mDelivery = delivery;
- }
Here comes a new face, NetworkDispatcher. Note the literal meaning of the array length parameter threadPoolSize. Combined with the above Volley architecture diagram, we can guess that NetworkDispatcher is a worker thread that waits in a loop and executes requests on the Queue through the network. After RequestQueue is instantiated, its start() method is called: - public void start() {
- stop();
-
- mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
- mCacheDispatcher.start();
-
-
- for ( int i = 0 ; i < mDispatchers.length; i++) {
- NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
- mCache, mDelivery);
- mDispatchers[i] = networkDispatcher;
- networkDispatcher.start();
- }
- }
-
- Accordingly:
-
- public void stop() {
- if (mCacheDispatcher != null ) {
- mCacheDispatcher.quit();
- }
- for ( int i = 0 ; i < mDispatchers.length; i++) {
- if (mDispatchers[i] != null ) {
- mDispatchers[i].quit();
- }
- }
- }
The logic here is simple: Stop all old tasks before starting (i.e. interrupt all worker threads). Start a worker thread responsible for cache. Start n worker threads responsible for the network. The worker thread starts to wait continuously for requests from the Queue. Request Next, execute queue.add(stringRequest); and a request is added to the queue. The code is as follows: - public <T> Request<T> add(Request<T> request) {
-
- request.setRequestQueue( this );
- synchronized (mCurrentRequests) {
- mCurrentRequests.add(request);
- }
-
-
- request.setSequence(getSequenceNumber());
- request.addMarker( "add-to-queue" );
-
-
- if (!request.shouldCache()) {
- mNetworkQueue.add(request);
- return request;
- }
-
-
- synchronized (mWaitingRequests) {
- String cacheKey = request.getCacheKey();
- if (mWaitingRequests.containsKey(cacheKey)) {
-
- Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
- if (stagedRequests == null ) {
- stagedRequests = new LinkedList<Request<?>>();
- }
- stagedRequests.add(request);
- mWaitingRequests.put(cacheKey, stagedRequests);
- if (VolleyLog.DEBUG) {
- VolleyLog.v( "Request for cacheKey=%s is in flight, putting on hold." , cacheKey);
- }
- } else {
-
-
- mWaitingRequests.put(cacheKey, null );
- mCacheQueue.add(request);
- }
- return request;
- }
- }
The logic here is: Make some settings for the newly added request. If cache is not needed, add the request directly to the network queue. Check if the request is being executed based on the key. If it is, put it into the waiting list. It is assumed that when the request is completed, a method will be called to delete the key from the waiting list, and then execute the waiting request in turn. If not, add it to the cache queue. CacheDispatcher Assuming that the uri access is the first execution, the corresponding request will be placed in the cache queue. When the cache worker thread (cache dispatcher) finds that there is a request in the cache queue, it will immediately dequeue and execute it. Let's take a look at the run method of CacheDispatcher: - public class CacheDispatcher extends Thread {
-
- private final Cache mCache;
-
- ...
-
- public void quit() {
- mQuit = true ;
- interrupt();
- }
-
- @Override
- public void run() {
- if (DEBUG) VolleyLog.v( "start new dispatcher" );
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-
-
- mCache.initialize();
-
- Request<?> request;
- while ( true ) {
-
- request = null ;
- try {
-
- request = mCacheQueue.take();
- } catch (InterruptedException e) {
-
- if (mQuit) {
- return ;
- }
- continue ;
- }
- try {
- request.addMarker( "cache-queue-take" );
-
-
- if (request.isCanceled()) {
- request.finish( "cache-discard-canceled" );
- continue ;
- }
-
-
- Cache.Entry entry = mCache.get(request.getCacheKey());
- if (entry == null ) {
- request.addMarker( "cache-miss" );
-
- mNetworkQueue.put(request);
- continue ;
- }
-
-
- if (entry.isExpired()) {
- request.addMarker( "cache-hit-expired" );
- request.setCacheEntry(entry);
- mNetworkQueue.put(request);
- continue ;
- }
-
-
- request.addMarker( "cache-hit" );
- Response<?> response = request.parseNetworkResponse(
- new NetworkResponse(entry.data, entry.responseHeaders));
- request.addMarker( "cache-hit-parsed" );
-
- if (!entry.refreshNeeded()) {
-
- mDelivery.postResponse(request, response);
- } else {
-
- request.addMarker( "cache-hit-refresh-needed" );
- request.setCacheEntry(entry);
-
-
- response.intermediate = true ;
-
-
-
- final Request<?> finalRequest = request;
- mDelivery.postResponse(request, response, new Runnable() {
- @Override
- public void run() {
- try {
- mNetworkQueue.put(finalRequest);
- } catch (InterruptedException e) {
-
- }
- }
- });
- }
- } catch (Exception e) {
- VolleyLog.e(e, "Unhandled exception %s" , e.toString());
- }
- }
- }
- }
Next, let’s take a look at the mDelivery.postResponse method. ExecutorDelivery From the above, we know that mDelivery is an instance of ExecutorDelivery (passed in when creating a new RequestQueue). The initialization code of ExecutorDelivery is as follows: - public ExecutorDelivery( final Handler handler) {
-
- mResponsePoster = new Executor() {
- @Override
- public void execute(Runnable command) {
- handler.post(command);
- }
- };
- }
For more information about java.util.concurrent.Executor, please refer to this article, which will not be expanded here. The postResponse code is as follows: - @Override
- public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
- request.markDelivered();
- request.addMarker( "post-response" );
- mResponsePoster.execute( new ResponseDeliveryRunnable(request, response, runnable));
- }
-
- ResponseDeliveryRunnable is a subclass of ExecutorDelivery, which is responsible for calling the corresponding listener method according to the different results of the request:
-
- @SuppressWarnings ( "rawtypes" )
- private class ResponseDeliveryRunnable implements Runnable {
-
- private final Request mRequest;
- private final Response mResponse;
- private final Runnable mRunnable;
-
- public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
- mRequest = request;
- mResponse = response;
- mRunnable = runnable;
- }
-
- @SuppressWarnings ( "unchecked" )
- @Override
- public void run() {
-
- if (mRequest.isCanceled()) {
- mRequest.finish( "canceled-at-delivery" );
- return ;
- }
-
-
- if (mResponse.isSuccess()) {
- mRequest.deliverResponse(mResponse.result);
- } else {
- mRequest.deliverError(mResponse.error);
- }
-
-
-
- if (mResponse.intermediate) {
- mRequest.addMarker( "intermediate-response" );
- } else {
- mRequest.finish( "done" );
- }
-
-
- if (mRunnable != null ) {
- mRunnable.run();
- }
- }
- }
Next, let's look back at how NetworkDispatcher handles the network queue. NetworkDispatcher The source code of NetworkDispatcher is as follows: - public class NetworkDispatcher extends Thread {
-
- private final Network mNetwork;
-
- ...
-
- private final BlockingQueue<Request<?>> mQueue;
-
- ...
-
- public void quit() {
- mQuit = true ;
- interrupt();
- }
-
- @TargetApi (Build.VERSION_CODES.ICE_CREAM_SANDWICH)
- private void addTrafficStatsTag(Request<?> request) {
- ...
- }
-
- @Override
- public void run() {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- Request<?> request;
- while ( true ) {
- long startTimeMs = SystemClock.elapsedRealtime();
-
- request = null ;
- try {
-
- request = mQueue.take();
- } catch (InterruptedException e) {
-
- if (mQuit) {
- return ;
- }
- continue ;
- }
-
- try {
- request.addMarker( "network-queue-take" );
-
-
-
- if (request.isCanceled()) {
- request.finish( "network-discard-cancelled" );
- continue ;
- }
-
- addTrafficStatsTag(request);
-
-
- NetworkResponse networkResponse = mNetwork.performRequest(request);
- request.addMarker( "network-http-complete" );
-
-
-
- if (networkResponse.notModified && request.hasHadResponseDelivered()) {
- request.finish( "not-modified" );
- continue ;
- }
-
-
- Response<?> response = request.parseNetworkResponse(networkResponse);
- request.addMarker( "network-parse-complete" );
-
-
-
- if (request.shouldCache() && response.cacheEntry != null ) {
-
- mCache.put(request.getCacheKey(), response.cacheEntry);
- request.addMarker( "network-cache-written" );
- }
-
-
- request.markDelivered();
- mDelivery.postResponse(request, response);
- } catch (VolleyError volleyError) {
- 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);
- }
- }
- }
-
- private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
- error = request.parseNetworkError(error);
- mDelivery.postError(request, error);
- }
- }
The processing flow of NetworkDispatcher is similar to that of CacheDispatcher, see the comments. An introduction to TrafficStats can be found here. The key to the above code lies in the two calls mNetwork.performRequest(request) and request.parseNetworkResponse(networkResponse). - Network
-
- Network is an interface with only one performRequest(Request<?> request) method:
-
- public interface Network {
-
- public NetworkResponse performRequest(Request<?> request) throws VolleyError;
-
- }
The implementation class of Network in this example is BasicNetwork: - public class BasicNetwork implements Network {
- protected static final boolean DEBUG = VolleyLog.DEBUG;
- private static int SLOW_REQUEST_THRESHOLD_MS = 3000 ;
- private static int DEFAULT_POOL_SIZE = 4096 ;
- protected final HttpStack mHttpStack;
- protected final ByteArrayPool mPool;
-
- public BasicNetwork(HttpStack httpStack) {
-
-
- this (httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
- }
- ...
- }
-
- Note the two key members of BasicNetwork: mHttpStack and mPool, and the dependency on apache:
-
- import org.apache.http.Header;
- import org.apache.http.HttpEntity;
- import org.apache.http.HttpResponse;
- import org.apache.http.HttpStatus;
- import org.apache.http.StatusLine;
But let's first look at the execution flow of performRequest(): - public NetworkResponse performRequest(Request<?> request) throws VolleyError {
- long requestStart = SystemClock.elapsedRealtime();
- while ( true ) {
-
- HttpResponse httpResponse = null ;
- byte [] responseContents = null ;
- Map<String, String> responseHeaders = Collections.emptyMap();
- try {
-
- Map<String, String> headers = new HashMap<String, String>();
- addCacheHeaders(headers, request.getCacheEntry());
-
- httpResponse = mHttpStack.performRequest(request, headers);
-
- StatusLine statusLine = httpResponse.getStatusLine();
- int statusCode = statusLine.getStatusCode();
-
- responseHeaders = convertHeaders(httpResponse.getAllHeaders());
-
- if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
-
- Entry entry = request.getCacheEntry();
- if (entry == null ) {
- return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null ,
- responseHeaders, true ,
- SystemClock.elapsedRealtime() - requestStart);
- }
-
-
- entry.responseHeaders.putAll(responseHeaders);
- return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
- entry.responseHeaders, true ,
- SystemClock.elapsedRealtime() - requestStart);
- }
-
-
- if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
- String newUrl = responseHeaders.get( "Location" );
- request.setRedirectUrl(newUrl);
- }
-
-
-
- if (httpResponse.getEntity() != null ) {
-
- responseContents = entityToBytes(httpResponse.getEntity());
- } else {
-
-
- responseContents = new byte [ 0 ];
- }
-
-
- long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
- logSlowRequests(requestLifetime, request, responseContents, statusLine);
-
- if (statusCode < 200 || statusCode > 299 ) {
- throw new IOException();
- }
- return new NetworkResponse(statusCode, responseContents, responseHeaders, false ,
- SystemClock.elapsedRealtime() - requestStart);
- } catch (SocketTimeoutException e) {
- attemptRetryOnException( "socket" , request, new TimeoutError());
- } catch (ConnectTimeoutException e) {
- 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) {
- attemptRetryOnException( "auth" ,
- request, new AuthFailureError(networkResponse));
- } else if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
- statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
- attemptRetryOnException( "redirect" ,
- request, new RedirectError(networkResponse));
- } else {
-
- throw new ServerError(networkResponse);
- }
- } else {
- throw new NetworkError(e);
- }
- }
- }
- }
The attemptRetryOnException() code is as follows: - 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));
- throw e;
- }
- request.addMarker(String.format( "%s-retry [timeout=%s]" , logPrefix, oldTimeout));
- }
RetryPolicy is an interface: - public interface RetryPolicy {
- public int getCurrentTimeout();
- public int getCurrentRetryCount();
- public void retry(VolleyError error) throws VolleyError;
- }
If not specified otherwise, the RetryPolicy in the request is DefaultRetryPolicy, and its retry method is implemented as follows: - public void retry(VolleyError error) throws VolleyError {
- mCurrentRetryCount++;
- mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
- if (!hasAttemptRemaining()) {
- throw error;
- }
- }
If the retry limit has not been exceeded, no exception will be thrown and the process will return to the while loop of performRequest(). Next, let's analyze the entityToBytes() method of BaseNetwork: - private byte [] entityToBytes(HttpEntity entity) throws IOException, ServerError {
-
- PoolingByteArrayOutputStream bytes =
- new PoolingByteArrayOutputStream(mPool, ( int ) entity.getContentLength());
- byte [] buffer = null ;
- try {
- InputStream in = entity.getContent();
- if (in == null ) {
- throw new ServerError();
- }
-
- buffer = mPool.getBuf( 1024 );
- int count;
-
- while ((count = in.read(buffer)) != - 1 ) {
-
- bytes.write(buffer, 0 , count);
- }
-
- return bytes.toByteArray();
- finally
- try {
-
- entity.consumeContent();
- } catch (IOException e) {
-
-
- VolleyLog.v( "Error occurred when calling consumingContent" );
- }
-
- mPool.returnBuf(buffer);
- bytes.close();
- }
- }
-
- The execution steps are described in the code comments. The ByteArrayPool class and PoolingByteArrayOutputStream are not expanded here.
- HttpStack
-
- HttpStack is an interface that is only responsible for sending the request:
-
- public interface HttpStack {
- public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
- throws IOException, AuthFailureError;
- }
From the initial analysis of the Volley class, we can see that HurlStack (java.net.HttpURLConnection) is used when SDK version > 9, otherwise HttpClientStack (org.apache.http.client.HttpClient) is used. Each stack implements the performRequest() method, which formally initiates an HTTP request internally. For specific usage, please refer to their respective API documents, which will not be elaborated here. Request The Request class mainly stores the parameters of the request and the current status of the request, and does not contain any request-related behavior: - public abstract class Request<T> implements Comparable<Request<T>> {
-
- ...
-
- public interface Method {
- int DEPRECATED_GET_OR_POST = - 1 ;
- int GET = 0 ;
- int POST = 1 ;
- int PUT = 2 ;
- int DELETE = 3 ;
- int HEAD = 4 ;
- int OPTIONS = 5 ;
- int TRACE = 6 ;
- int PATCH = 7 ;
- }
-
- ...
-
- private final int mMethod;
- private final String mUrl;
- private String mRedirectUrl;
- private String mIdentifier;
- private final int mDefaultTrafficStatsTag;
- private Response.ErrorListener mErrorListener;
- private Integer mSequence;
- private RequestQueue mRequestQueue;
- private boolean mShouldCache = true ;
- private boolean mCanceled = false ;
- private boolean mResponseDelivered = false ;
- private RetryPolicy mRetryPolicy;
-
- ...
- }
Let's analyze the request.parseNetworkResponse(networkResponse) method. Take StringRequest as an example: - @Override
- protected Response<String> parseNetworkResponse(NetworkResponse response) {
- String parsed;
- try {
- parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
- } catch (UnsupportedEncodingException e) {
- parsed = new String(response.data);
- }
- return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
- }
-
- You can see that it simply converts the data into a string and then returns a success response.
-
- The implementation of JsonObjectRequest is as follows:
-
- @Override
- protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
- try {
- String jsonString = new String(response.data,
- HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
- return Response.success( new JSONObject(jsonString),
- HttpHeaderParser.parseCacheHeaders(response));
- } catch (UnsupportedEncodingException e) {
- return Response.error( new ParseError(e));
- } catch (JSONException je) {
- return Response.error( new ParseError(je));
- }
- }
It now converts data into a string and then generates a JSONObject to return. Summarize In summary, the general framework of Volley is as follows: A RequestQueue contains two internal queues, namely the cache queue and the network queue. There is also a cache dispatcher and n network dispatchers, which are inherited from Thread and are responsible for executing cache and network requests respectively. There is also a delivery, which is responsible for distributing request results. The cache dispatcher runs on a separate thread. The cache dispatcher loops around waiting, fetching, and executing requests from the cache queue, and passing the results to the delivery. N network dispatchers run on independent threads. The network dispatcher waits in a loop, retrieves and executes requests from the network queue, and sends the results to delivery and adds them to the cache. Delivery is responsible for passing the results to the corresponding listener callbacks on the main thread. |