I promised you last week, a web request framework. After learning the basics of Java for nearly two months, now we are going to practice with a network request framework. Handwritten a network request framework needs to master more knowledge points, which involves design patterns, collections, generics, multithreading and concurrency, network programming and other knowledge, is a comprehensive examination of Java basic skills, at the same time, the ability to architecture also has certain requirements.

demand

Let’s take a look at the demand

  • Support request JSON text typology, audio, image type, batch download. Upload ~
  • When requesting various data, the calling layer does not care about the encapsulation of upload parameters
  • Once the data is retrieved, the calling layer does not care about parsing the JSON data
  • When calling back, the calling layer only needs to know the corresponding response class of the incoming JSON
  • The result of the callback response occurs on the main thread (thread switch)
  • To download, upload the extension
  • Supports high concurrency requests. The request queue is obtained at a time. You can set the maximum number of concurrent requests

Architecture design

First let’s review the flow of a network request.

1. Prepare a request 2. Initiate a network request based on the parameters of the request 3

And then the code we call looks something like this

Volley.sendRequest(null, url, GankResponse.class, GET, new IDataListener<GankResponse>() {
        @Override
        public void onSuccess(GankResponse response) {}

        @Override
        public void onFail(int errorCode, String errorMsg) {}
});Copy the code

Ha ha, I just imagined it. I didn’t actually use Volley.

Well, our ultimate goal is to write code like this.

We’ve just said that a network request is about three steps, and we’re going to design the network architecture based on these three steps. Type on the blackboard and remember these three stages.

Stage 1: Prepare the request

1. Prepare parameters

This stage is usually initiated from the Activity interface. To put it bluntly, it seems that MY client page does not know what kind of data to display, so I go to the server to ask for it. A network request is actually an interaction with the service area, and during the interaction, according to the HTTP protocol (as mentioned earlier, if you don’t understand, go back to my article), we need to prepare the following parameters

  • The url address
  • RequestParams Request parameters
  • Request methods (GET, POST, etc.)
  • ResponseBean The bean that receives the response
  • Request the result callback to the activity (IDataListener①)

The first three parameters are used to make the network request and the last two parameters are used to make the request response.

Therefore, we need a bean (RequestHolder ②) to encapsulate the request parameters. RequestHolder requires the following properties

  • requestParams
  • url
  • Request method type (GET, POST)

We then need a class (IHttpService ③) to initiate the network request, which requires at least the following attributes

  • RequestHolder instance
  • The excute method performs network requests
  • IHttpListener④ (holds an instance of the request result handling class)

    So the IHttpListener, which belongs to stage 3, let’s look at the IHttpListener.

Phase 3: Request result processing and callback IHttpListener

Let’s skip stage two.

As mentioned above, the IHttpService holds the IHttpListener and handles and calls back the IHttpListener when the request ends. There are two kinds of requests, success and failure. So the class only needs to have two behaviors (methods) and one attribute

  • onSuccess
  • onFail
  • Holds an instance of IDataListener
  • Analytical data

  • OnSuccess IHttpService calls this method after a successful network request, IHttpListener needs to parse the data (such as json transmission, InputStream conversion to image, File, etc.) and then call IDataListener to return it to the activity

  • OnFail IHttpService This method is called when a network request fails. IDataListener directly calls IDataListener to return an error message to the activity

  • This step takes place after the request succeeds in retrieving the source data, and then performs different parses based on the type of the source data. Finally, call IHttpListener to return the data to the activity.

Phase 2: Initiates a network request based on the request parameters

According to our design, the actual initiation of a network request is the excuse method of IHttpService. We can regard a network request as an asynchronous task, so we need a class to implement the Runnable interface, and then call the excuse of IHttpService in the RUN method to execute the network request. For the moment, we’ll call this class HttpTask⑤, which implements the Runnable interface. This class is relatively simple and only needs to hold an instance of IHttpService.

  • IHttpService instance

Then for an asynchronous task, HttpTask, we need to do a concurrency management, so we need to introduce a thread pool to manage all of the HttpTasks. * If the thread pool is set up by the thread pool, go to the following file: * If the thread pool is set up by the thread pool, go to the following file: * If the thread pool is set up by the thread pool, go to the following file: * If the thread pool is set up by the thread pool, go to the following file: Constraints on how the maximum concurrency, blocking, production consumer model is implemented can be traced back to Java technology threads.

ThreadPoolManger

  • The ExecutorService instance

Volley class

Ok, now that we have almost everything we need for our network request, we can now create classes like RequestHolder, IHttpService, IHttpListener, and HttpTask in our Activity to initiate a network request. But it seems like there are a lot of things to create, and many of them are not things that the caller needs to care about and are error-prone. For the client, I only need to prepare request parameters and corresponding parameter types according to the requirements of the server to receive the response of the server, and do not need to care about the request process. So here we create a Volley⑦ class to encapsulate the request and response-related classes and throw an HttpTask into the thread pool. This class is simple, requiring only a static sendRequest method with parameters passed in to the URL, requestParams, and so on.

  • sendRequest

summary

The logic is a little messy, so let me get the logic straight. To start with the Activity, there is a requirement to interact with the server. The steps are as follows:

1. Call Volley’s static method sendRequest and pass in url, RequestParams and other parameters required by the method. 2.Volley’s sendRequest method constructs RequestHolder, IHttpService, IHttpListener, HttpTask and other classes according to the method parameters. Throw the HttpTask into the thread pool of ThreadPoolManger for execution. 3. The thread pool of ThreadPoolManger is actually a blocking queue that pulls out the HttpTask based on the task and executes it. 4. The HttpTask run method is invoked. The RUN method invokes the IHttpService excuse method to initiate a network request. 5. When the network request ends, IHttpService calls IHttpListener’s success/failure methods to process the request. 6. If the network request succeeds, IHttpListener parses the source data and calls back the parsed data to the activity through IDataListener. 7. If the network request fails, IHttpListener invokes IDataListener’s fail method to inform the activity that the request failed.

Oh, by the way, I forgot to mention thread switching, but think about it, which class should I cut back to the main thread? I’ll show you the answer in the code.

This is what the architecture looks like, and I’ve drawn a class diagram to make it easier for you to understand.

Fill in the pit

Before blowing to you, said what requirements will be implemented, which knowledge points, which design patterns, before the code, I will explain to you.

  • Support JSON text type, audio type, image type, batch download, upload ~ Support to request JSON class, image, audio and other resources download, this is reserved for expansion. We just need to make different implementations depending on the data type of the request, and each implementation needs to inherit the IHttpService interface.

  • Request a variety of data, called don’t try to upload parameters of encapsulation direct call public static IHttpService the sendRequest (requestParams, url, responseClass, type, the listener) method.

  • After retrieving the data, the calling layer does not care about the parsing of the JSON data. The parsing of the data is the same as IHttpService. It just needs to do different IHttpListener implementations for different data types.

  • When calling back, the calling layer only needs to know that the corresponding response class of the incoming JSON uses generics and will convert the JSON data into the bean of the response class in IHttpListener’s JSON data implementation class.

  • The result of the callback response occurs when the main thread (thread switch) is implemented in the implementation class of IHttpListener.

  • There are interfaces reserved for download and upload extensions, and you only need to extend the IHttpService and IHttpListener interfaces, which will be explained in the next article.

  • Support high concurrency request, request queue once, can set the maximum concurrency, set first request first execution through the thread pool.

I’m going to use
  • Generic request parameters and callback parameters are generic implementations
  • * / * create a Thread pool by calling the Concurrent package Executors. / / Create a Thread pool by using LinkedBlockingQueue.

  • The blocking queue LinkedBlockingQueue is a blocking queue, as described in the Concurrent toolkit.

  • Thread rejection policy to be implemented

Design patterns used
  • Template method pattern

    Template method: Defines a framework for an algorithm in an operation, deferring some steps to subclasses. The template method pattern allows subclasses to redefine specific steps of an algorithm without changing the structure of that algorithm. IHttpListener (IHttpListener, IHttpListener, IHttpListener, IHttpListener, IHttpListener)

  • The singleton pattern ThreadPoolManger is a singleton

  • Policy mode Depending on the request data, choose to use either IHttpService JSonHTTPService or FileDownHttpService (this class is in the file download module)

  • The producer-consumer thread pool itself is a producer-consumer model. The Activity makes a request, Volley produces the request as an HttpTask and throws it into the thread pool, and then the request ends up consuming the request.

Lu code

The architecture is designed, the class diagram is designed, and we start coding.

Where to start? You can choose to start masturbation by calling Volley from the activity in a logical order. However, I do not recommend this method because it is error-prone and inefficient.

First, let’s take a look at some of the key class objects I marked during the analysis above

①IDataListener

②RequestHolder

③IHttpService

④IHttpListener

⑤HttpTask

⑥ThreadManger

⑦Volley

Now, let’s do the code in that order.

IDataListener

This class is very simple, just need to do a request result callback.

Public interface IDataListener<T> {/** * @param T response parameter */ void onSuccess(T T); void onFail(int errorCode, String errorMsg); }Copy the code

Where T is a generic operation, because we also don’t know what type of data structure the result will be.

RequestHolder

Now we’re going to have a problem with the class, because the RequestHolder will hold references to IHttpService and IHttpListener, so we should first lift the two interfaces out.

This class is very simple, just need to hold IHttpService, IHttpListener, URL, requestParams, RequestHolder, etc., no logical operation.

Note: you only need to hold interface references to IHttpService and IHttpListener. You do not need to hold instance references to either interface.

IHttpService

Since the IHttpService is held by the RequestHolder and is the real network request-related class constructor and executor, we need to define and set the interface methods.

Public interface IHttpService {/** * set url ** @param URL URL address */ voidsetUrl(String url); /** * @param listener */ voidsetHttpListener(IHttpListener listener); /** * set request parameters ** @param data Request parameters byte array */ voidsetRequestData(byte[] data); /** * Perform the request */ void excute(); void cancel(); boolean isCancel(); voidsetRequestHeader(Map<String, String> map);

    void setRequestType(String type);

}Copy the code

Define the above interface methods, some parameters really do not bother to write comments, you should all understand.

Then in the IHttpService implementation class needs to do the specific Http request, here I am lazy, direct use of HttpClient. Concrete implementation class JsonHttpService you can go to download my source code to read.

HttpListener

This class is assigned to the IHttpService class to parse the data after the network request completes. Since there are only two results of the network request — success and failure, the method to parse the data is not defined here, because it is called after the network request succeeds.

Public interface IHttpListener {/** * network request callback successfully ** @param httpEntity Network request return result */ void onSuccess(httpEntity httpEntity);  void onFail(int errorCode, String errorMsg); }Copy the code

IHttpListener then holds the IDataListener instance and finally calls IDataListener back and forth to the Activity. By the way, there is a need to do thread switch oh, the specific implementation code is very simple, please download the source code to read.

HttpTask

When the entire network request is completed, we need a class that calls the excuse method of IHttpService to execute the network request, and the Excuse method must be in the child thread. We encapsulate a network request into an asynchronous task, a Runnable, and the code is simple.

public class HttpTask<T> implements Runnable {

    private IHttpService mHttpService;

    public HttpTask(RequestHolder<T> holder) throws UnsupportedEncodingException {
        mHttpService = holder.getHttpService();
        mHttpService.setHttpListener(holder.getHttpListener());
        mHttpService.setRequestType(holder.getRequestType());
        mHttpService.setUrl(holder.getUrl());
        mHttpService.setRequestHeader(holder.getRequestHeader());
        T requestParams = holder.getRequestParams();
        if(requestParams ! = null) { String requestInfo = JSON.toJSONString(requestParams); mHttpService.setRequestData(requestInfo.getBytes("UTF-8"));
        }
    }

    @Override
    public void run() {
        if(! mHttpService.isCancel()) { mHttpService.excute(); }}}Copy the code

ThreadPoolManger

Finally, we need a thread pool to manage HttpTask. So we need a ThreadPoolManger, because this class must be unique, so singleton. Create thread pool management by using the Concurrent package Executors class.

public class ThreadPoolManger {
    private static volatile ThreadPoolManger mInstance;
    private final ExecutorService mExecutorService;

    private ThreadPoolManger() {
        mExecutorService = Executors.newFixedThreadPool(4);
    }

    public static ThreadPoolManger getInstance() {
        if(mInstance == null) { synchronized (ThreadPoolManger.class) { mInstance = new ThreadPoolManger(); }}returnmInstance; } public void execute(HttpTask task) { mExecutorService.execute(task); }}Copy the code

Volley

Finally, create IHttpService, RequestHolder, IHttpListener, HttpTask, and throw the HttpTask into the thread pool. This sequence of operations is invisible to the caller and can be hidden, so a Volley class is needed to hide the process. The code is also simple:

public class Volley {
    static HashMap<String, String> mGlobalHeader = new HashMap<>();

    public static <T, M> IHttpService sendRequest(T requestParams, String url, Class<M> responseClass, @RequestType String type,IDataListener<M> listener) {
        IHttpService jsonHttpService = new JsonHttpService();

        IHttpListener jsonDealListener = new JsonDealListener<>(listener, responseClass);

        RequestHolder<T> requestHolder = new RequestHolder<>(requestParams, jsonHttpService, jsonDealListener, url,type);

        try {
            HttpTask<T> task = new HttpTask<>(requestHolder);
            ThreadPoolManger.getInstance().execute(task);
        } catch (UnsupportedEncodingException e) {
            listener.onFail(0, e.getMessage());
        }
        return jsonHttpService;
    }


    public static void setGlobalHeader(String key, String value) { mGlobalHeader.put(key, value); }}Copy the code

At this point, with the exception of the IHttpListener and IHttpService implementation classes that have not been posted, the entire network request architecture prototype is complete. Let’s test it out

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void test(View view) {
        String url = "http://gank.io/api/data/Android/10/1";
        for (int i = 0; i < 50; i++) {
            Volley.sendRequest(null, url, GankResponse.class, GET, new IDataListener<GankResponse>() {
                @Override
                public void onSuccess(GankResponse response) {
                    Log.e("_____"."Request successful");
                }

                @Override
                public void onFail(int errorCode, String errorMsg) {
                    Log.e("___error", errorMsg + "__errorCode:"+ errorCode); }}); }}}Copy the code

The above is a button clicked in MainActivity to do a concurrent operation of 50 network requests, and the test was successful.

Next up

Next time we will continue to do download extensions based on this framework.

There will be multi-threaded downloads, breakpoint continuation and other operations, if the time is enough, combined with the database to do some magic operations.

Oh, yeah, stick it on

The code address

If my article can help you, please remember to hit the star