• Request
  • Response
  • RequestQueue
  • NetworkDispatcher
  • HttpStack
  • UrlHttpStack
  • Delivery
  • SimpleVolley

Source address github

Request

First define the Request class (Request class)

The Request class requires the following attributes:

  • Url Indicates the requested URL
  • Method Indicates the METHOD of an HTTP request, such as GET, POST, HEAD, or others
  • Request header HTTP request headers, such as Content-Type and Accept
  • Request parameters For the POST method, you want to pass the request parameters to the server
  • Callback methods include callback methods after a request succeeds or fails

Why generic T? Because for network requests, the user gets the request result in an indeterminate format, which may be JSON, XML, String, etc. Void parseResponse2(Response

Response); void parseResponse2(Response

Response); , which can parse the binary data in the HTTP response entity into the specific type required by the user. So we can think of Request as a generic class whose generic type is its return data type, which is a String, so we use Request

.


package com.fan.simplevolley.volley;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/ * * *@Description: Base class encapsulation of the request@Author: fan
 * @Date: 2020/3/5 34 *@Modify: * /
public abstract class Request<T> implements Comparable<Request<T>> {

    /** * the requested URL */
    public String url;

    /** * the requested method */
    private Method method;

    /** * The default parameter encoding */
    private String DEFAULT_PARAMS_ENCODING = "UTF-8";

    /** * The sequence number of the request, which is used to sort the request queue */
    private int sequenceNumber;

    /** * The priority of the request, which is used to sort the request queue */
    private Priority priority;
    

    /** * request header */
    Map<String, String> headers = new HashMap<>();

    /** * Request parameters */
    Map<String, String> params = new HashMap<>();

    /** * request result callback */
    Response.RequestListener<T> listener;


    /**
     * constructor
     *
     * @param method
     * @param url
     * @param listener
     */
    public Request(Method method, String url, Response.RequestListener<T> listener) {
        this.method = method;
        this.url = url;
        this.listener = listener;
    }

    public Map<String, String> getHeaders(a) {
        return headers;
    }

    public Map<String, String> getParams(a) {
        return params;
    }

    public Method getHttpMethod(a) {
        return method;
    }

    protected String getParamsEncoding(a) {
        return DEFAULT_PARAMS_ENCODING;
    }

    public int getSequenceNumber(a) {
        return sequenceNumber;
    }

    public Priority getPriority(a) {
        return priority;
    }

    /** * Specifies the content-type of the request@return* /
    public String getBodyContentType(a) {
        return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
    }


    /** * If it is a GET request, the parameters are concatenated after the URL and there is no request entity * if it is a POST request, the request parameters are placed inside the request entity * <p> * This method is used to GET the specified request parameters and encode them in the specified format, generating a byte array. For POST and PUT requests, the contents of this byte array are sent as the request entity * *@return* /
    public byte[] getBody() {
        // The getParams() method is called to get the specified argument
        // polymorphic, adds a subclass of Request to the RequestQueue, and calls the subclass's method if it overrides it
        Map<String, String> params = getParams();
        if(params ! =null && params.size() > 0) {
            return encodeParameters(params, getParamsEncoding());
        }
        return null;
    }

    /** * convert the argument to a URL-encoded argument string of the form key=value&key2=value2, but be sure to use URLEncoder encoding. You need to specify content-type as Application /x-www-form-urlencoded */
    private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
        StringBuilder encodedParams = new StringBuilder();
        try {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
                encodedParams.append('=');
                encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
                encodedParams.append('&');
            }
            return encodedParams.toString().getBytes(paramsEncoding);
        } catch (UnsupportedEncodingException uee) {
            throw new RuntimeException("Encoding not supported: "+ paramsEncoding, uee); }}/** * parse the response into the desired data object **@param response
     * @return* /
    abstract T parseResponse(Response<T> response);

    abstract void parseResponse2(Response<T> response);

    /** * posts parsed data objects to the UI thread **@param response
     */
    abstract void deliverResponse(T response);

    /** ** error **@param error
     */
    void deliverError(VolleyError error) {
        if(listener ! =null) { listener.onError(error); }}@Override
    public int compareTo(Request<T> another) {
        Priority myPriority = this.getPriority();
        Priority anotherPriority = another.getPriority();
        // If the priorities are equal, the sequence numbers are added to the queue
        return myPriority.equals(another) ? this.getSequenceNumber() - another.getSequenceNumber()
                : myPriority.ordinal() - anotherPriority.ordinal();
    }


    /** * the requested method enumerates */
    public enum Method {
        GET("GET"),
        POST("POST");

        private String method = "";

        private Method(String method) {
            this.method = method;
        }

        @Override
        public String toString(a) {
            returnmethod; }}/** * Request priority enumeration */
    public enum Priority {
        LOW,
        NORMAL,
        HIGH
    }

}

Copy the code

Response

According to the HTTP protocol, the HTTP response packet structure is as follows:

There is a BasicHttpResponse class in the org.apache. HTTP package. This class helps us to maintain the related content of ① packet protocol and version ② status code and state description in THE HTTP response message. Therefore, we inherit directly from it and maintain the ③ response header and ④ response entity in the HTTP response message. So our Response class declares public class Response

extends BasicHttpResponse {} and adds the following two variables:

  1. byte[] rawData; Raw binary data in the response entity

  2. public T result; The object that will respond to the resolved entity

Tip1: For simplicity, we have ignored the handling of the response header.

Tip2: Why is Response made generic?

Because each request corresponds to a Response, but the problem here is that we do not know the data format of this Response. This data format should be consistent with Request’s generic T, so we’ll write Response as generic.

package com.fan.simplevolley.volley;

import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.message.BasicHttpResponse;

/ * * *@Description: Encapsulates the response class *@Author: fan
 * @Date: 2020/3/5 34 *@Modify: * /
public class Response<T> extends BasicHttpResponse {

    /** * Response entity data */
    byte[] rawData;

    /** * will respond to the object */ after entity resolution
    public T result;

    public Response(StatusLine statusline) {
        super(statusline);
    }

    public Response(ProtocolVersion ver, int code, String reason) {
        super(ver, code, reason);
    }

    public Response<T> success(T parsed) {
        result = parsed;
        return this;
    }

    public void setData(byte[] rowData) {
        this.rawData = rowData;
    }

    public void setResult(T result) {
        this.result = result;
    }

    public interface RequestListener<T> {

        void onSuccess(T result);

        void onError(VolleyError error); }}Copy the code

Now that you have the base class Request for the Request and the Response class Response, you can begin the concrete workflow.

The general process is as follows:

  1. To create aRequestObject that specifies the various data, URLS, methods, callback methods, and so on to be requested.
  2. Add the Request object to the queue.
  3. The network processing thread handles the network request and returns the data.
  4. Leave it to the user for reprocessing (such as updating the UI).

Let’s see how it’s done.

Now that we have the Request, let’s look at the Request queue.

RequestQueue

We maintain a blocking queue in the RequestQueue class, PriorityBlockingQueue

> blockingQueue = new PriorityBlockingQueue<>(); Is used to hold requests that you want to execute.
>

When we need a Request, we throw the constructed Request object into this queue.

The network processing thread then continually pulls the Request object from the Request queue to make the actual network Request.

package com.fan.simplevolley.volley;

import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
/ * * *@DescriptionRequest queue encapsulation *@Author: fan
 * @Date: 2020/3/5 34 *@Modify: * /
public class RequestQueue {

    /** * blocked request queue */PriorityBlockingQueue<Request<? >> blockingQueue =new PriorityBlockingQueue<>();

    /** * Serial number generator */ for each request
    AtomicInteger sequenceNumberGenerator = new AtomicInteger();

    /** * Default number of threads to execute network requests */
    public static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

    /** * self-maintained network request thread */
    NetworkDispatcher[] networkDispatchers;

    /** * actually executes the network request */
    HttpStack httpStack;

    /**
     * constructor
     *
     * @param threadPoolSize
     * @param httpStack
     */
    public RequestQueue(int threadPoolSize, HttpStack httpStack) {
        networkDispatchers = new NetworkDispatcher[DEFAULT_NETWORK_THREAD_POOL_SIZE];
        this.httpStack = httpStack ! =null ? httpStack : new UrlHttpStack();
    }


    /** ** * /** ** /** * That's not considered here. * /
    public void add(Request
        request) {
        if(! blockingQueue.contains(request)) { blockingQueue.add(request); }else {

            System.out.println("The request is already in the queue, please do not add again"); }}public void start(a) {

        stop();

        for (int i = 0; i < DEFAULT_NETWORK_THREAD_POOL_SIZE; i++) {
            networkDispatchers[i] = newNetworkDispatcher(blockingQueue, httpStack); networkDispatchers[i].start(); }}public void stop(a) {

        if(networkDispatchers! =null)
        for (int i = 0; i < networkDispatchers.length; i++) {
            if(networkDispatchers[i] ! =null) { networkDispatchers[i].quit(); }}}}Copy the code

Let’s look at network processing threads.

NetworkDispatcher

It is essentially a Thread, a worker Thread (in an infinite loop) that continually pulls Request objects from the queue and uses HttpStack to actually execute the Request.

package com.fan.simplevolley.volley;

import java.util.concurrent.BlockingQueue;

/ * * *@DescriptionThe network Request thread, which is responsible for: * 1. Fetching the Request from the Request queue (requires holding a reference to blockingQueue in the RequestQueue) * 2. Execute a network request using HttpStack and get the result (holding a reference to HttpStack) * 3. Post the response to the UI thread for processing (requires a sender to deliver) *@Author: fan
 * @Date: 2020/3/5 34 *@Modify: * /
public class NetworkDispatcher extends Thread {

    /** * Whether to exit */
    boolean quit;

    /** ** actually executes the network request */
    HttpStack httpStack;

    /** * the queue that holds the request */BlockingQueue<Request<? >> blockingQueue;/** * posts the response to the sender of the UI thread */
    Delivery delivery = new Delivery();

    /**
     * constructor
     *
     * @param blockingQueue
     * @param httpStack
     */NetworkDispatcher(BlockingQueue<Request<? >> blockingQueue, HttpStack httpStack) {this.httpStack = httpStack;
        this.blockingQueue = blockingQueue;
    }


    @Override
    public void run(a) {
        // In an endless loop, requests are continually pulled from the blocking queue to execute
        while (true) { Request<? > request;try {
                // Take a request from the queue.
                request = blockingQueue.take();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (quit) {
                    return;
                }
                continue;
            }

            Response response = null;
            try {

                // Call httpStack to execute the actual network request. At this point, the rawData in the response has been stored in the entity of the response
                response = httpStack.performRequest(request);

                / / way 1:
                //Object o = request.parseResponse(response);
                //response.setResult(o);

                / / way 2:
                request.parseResponse2(response);

                delivery.postResponse(request, response);
            } catch (Exception e) {
                e.printStackTrace();
                delivery.postError(request, newVolleyError(e)); }}}public void quit(a) {
        quit = true; interrupt(); }}Copy the code

HttpStack

package com.fan.simplevolley.volley;

/ * * *@Description: an interface that performs network requests, and its subclasses need to implement the performRequest() method to actually make network requests *@Author: fan
 * @Date: 2020/3/5 34 *@Modify: * /
public interface HttpStack {
    publicResponse<? > performRequest(Request<? > request)throws Exception;
}

Copy the code

HttpStack is an interface, and we constructed an implementation class UrlHttpStack

UrlHttpStack

Inside UrlHttpStack’s performRequest method, the Request object is first read from the user set about the Request parameters, including the URL, is the GET method or POST method, Request header, Request parameters. After obtaining these parameters, we set these parameters to the HttpUrlConnection object, and then the HttpUrlConnection object actually initiates the request to retrieve the data.

After the request is returned, we can get the corresponding status code, state description, Response entity and other relevant information, which can be used to construct a Response object. At this point, the status code, state description and Response entity data in the Response object have been assigned values, and only the object stored after parsing is public T result; It hasn’t been assigned yet. At this point we call the Request parseResponse2() method to parse the binary data in the response entity into the required type and store it in T result; In the.

package com.fan.simplevolley.volley;

import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.message.BasicStatusLine;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Set;

/ * * *@Description: a tool that uses URLConnection to make network requests@Author: fan
 * @Date: 2020/3/5 34 *@Modify: * /
public class UrlHttpStack implements HttpStack {
    @Override
    publicResponse<? > performRequest(Request<? > request)throws Exception {
        URL newURL = new URL(request.url);
        HttpURLConnection connection = (HttpURLConnection) newURL.openConnection();
        connection.setDoInput(true);
        connection.setUseCaches(false);

        Set<String> headersKeys = request.getHeaders().keySet();
        for (String headerName : headersKeys) {
            connection.addRequestProperty(headerName, request.getHeaders().get(headerName));
        }

        //request.getParams();

        Request.Method method = request.getHttpMethod();
        connection.setRequestMethod(method.toString());
        // add params
        byte[] body = request.getBody();
        if(body ! =null) {
            // enable output
            connection.setDoOutput(true);
            // set content type
            connection
                    .addRequestProperty("Content-Type", request.getBodyContentType());
            // write params data to connection
            DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
            dataOutputStream.write(body);
            dataOutputStream.close();
        }

// connection.connect();

        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP".1.1);
        int responseCode = connection.getResponseCode();
        if (responseCode == -1) {
            throw new IOException("Could not retrieve response code from HttpUrlConnection.");
        }
        // Status line data
        StatusLine responseStatus = new BasicStatusLine(protocolVersion,
                connection.getResponseCode(), connection.getResponseMessage());
        / / build the responseResponse<? > response =new Response(responseStatus);
        // Set response data
        //BasicHttpEntity entity = new BasicHttpEntity();
        InputStream inputStream = null;
        try {
            inputStream = connection.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
            inputStream = connection.getErrorStream();
        }

        int len = -1;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] n = new byte[1024];
        while((len = inputStream.read(n)) ! = -1) {
            baos.write(n, 0, len);
        }
        baos.flush();

        response.setData(baos.toByteArray());
        returnresponse; }}Copy the code

The request is complete and delivered to the UI thread

Delivery

The NetworkDispatch object holds a default Delivey, and when the request ends, the Delivery Handler(Handler Handler = new Handler(looper.getmainlooper ());) Send the request result Response object to the UI thread.

Dispatch means calling the Request callback method in the UI thread. It was finished.

package com.fan.simplevolley.volley;

import android.os.Handler;
import android.os.Looper;

/ * * *@Description: a deliverer that posts response data from the worker thread to the UI thread. Use Android Handler to implement *@Author: fan
 * @Date: 2020/3/5 34 *@Modify: * /
public class Delivery {

    Handler handler = new Handler(Looper.getMainLooper());

    public void postResponse(final Request request, final Response response) {
        handler.post(new Runnable() {
            @Override
            public void run(a) {

                // Deliver a normal response or error, depending.
                // On success, call the success callback set by Request
                // Here deliverResponse just wraps another layer on request.callback, nothing elserequest.deliverResponse(response.result); }}); }public void postError(final Request request, final VolleyError error) {
        handler.post(new Runnable() {
            @Override
            public void run(a) { request.deliverError(error); }}); }}Copy the code

SimpleVolley

The main flow of the network Request is finished. Finally, we need to figure out how to add the constructed Request to the Request queue. See Volley

In SimpleVolley, create a singleton RequestQueue.

package com.fan.simplevolley.volley;

/ * * *@DescriptionThis is a simple implementation of the Volley Network request framework. I call it simple-volley. *@Author: fan
 * @Date: 2020/3/5 34 *@Modify: * /
public class SimpleVolley {

    /** * Create a RequestQueue, like volley.newRequestQueue (); *@return* /
    public static RequestQueue newRequestQueue(a) {
        return newRequestQueue(RequestQueue.DEFAULT_NETWORK_THREAD_POOL_SIZE);
    }

    /** * Create a request queue with NetworkExecutor number coreNums **@param coreNums
     * @return* /
    public static RequestQueue newRequestQueue(int coreNums) {
        return newRequestQueue(coreNums, null);
    }

    public static RequestQueue newRequestQueue(int coreNum, HttpStack httpStack) {

        RequestQueue requestQueue = new RequestQueue(coreNum, httpStack);
        requestQueue.start();
        returnrequestQueue; }}Copy the code

Specific use:

    // create a RequestQueue corresponding to volley.newRequestQueue ();
    RequestQueue requestQueue = SimpleVolley.newRequestQueue();

    StringRequest request = new StringRequest(Request.Method.GET, "http://www.baidu.com".new Response.RequestListener<String>() {
                    @Override
                    public void onSuccess(String result) {
                        System.out.println(result);
                        resultTextView.setText(result);
                    }

                    @Override
                    public void onError(VolleyError error) { System.out.println(error.getMessage()); resultTextView.setText(error.getMessage()); }}); requestQueue.add(request);Copy the code

Source address github