How to write the distribution and execution of Http requests in the Android network framework.

Source: Internet
Author: User

How to write the distribution and execution of Http requests in the Android network framework.


Preface

In the first two blogs in the "teach you to write an Android network framework" column, we have introduced the basic structure of the SimpleNet framework and the implementation of Request, Response, and Request queue, and why is this design taken into consideration. The previous two blogs have introduced various roles. Today we will analyze several other important roles, including NetworkExecutor, HttpStack, and ResponseDelivery, they correspond to network request threads, Http actuators, and Response distribution, which are the core of http request execution and Response Processing.

Let's look back at the division of labor and cooperation among the various roles of SimpleNet. First, you need to create a request queue and add each request to the Request queue. Multiple networkexecutors (essentially a thread) share a message queue. In each NetworkExecutor, they cyclically retrieve requests from the request queue, obtain a request, and then execute Http requests through HttpStack, after the request is complete, the Response result is finally distributed to the UI thread through ResponseDelivery to ensure that the request callback is executed in the UI thread, so that the user can directly update the UI in the callback. Execution Process 1.


Figure 1

If you are not familiar with the architecture diagram, refer to the first blog in the column.

NetworkExecutor

NetworkExecutor plays an important role as the "heart" in SimpleNet. It is called "Heart" because NetworkExecutor is used to continuously obtain requests from the request queue and then deliver them to HttpStack for execution. It is like an engine in a car and the heart in a human body, driving the operation of the entire frame.

NetworkExecutor is essentially a Thread. In the run method, we will execute a loop, constantly get requests from the request queue, and then hand the requests to HttpStack. Because it is relatively simple, let's directly go to the code.

/*** Network request Executor, inherited from Thread, read requests cyclically from the network Request queue and execute ** @ author mrsimple */final class NetworkExecutor extends Thread {/*** network Request queue */private BlockingQueue <Request <?> MRequestQueue;/*** network request stack */private HttpStack mHttpStack;/*** result distributor, ship the result to the main thread */private static ResponseDelivery mResponseDelivery = new ResponseDelivery ();/*** request Cache */private static Cache <String, response> mReqCache = new LruMemCache ();/*** stop? */private boolean isStop = false; public NetworkExecutor (BlockingQueue <Request <?> Queue, HttpStack httpStack) {mRequestQueue = queue; mHttpStack = httpStack;} @ Override public void run () {try {while (! IsStop) {final Request <?> Request = mRequestQueue. take (); if (request. isCanceled () {Log. d ("####", "### canceled"); continue;} Response response = null; if (isUseCache (request )) {// obtain response = mReqCache from the cache. get (request. getUrl ();} else {// get data from the network response = mHttpStack. required mrequest (request); // if the request needs to be cached, the request is cached to mResponseCache if (request. shouldCache () & isSuccess (response) {mReqCache. put (request. getUrl (), res Ponse) ;}}// the request result is mResponseDelivery. deliveryResponse (request, response) ;}} catch (InterruptedException e) {Log. I ("", "### request the distributor to exit") ;}} private boolean isSuccess (Response response) {return response! = Null & response. getStatusCode () = 200;} private boolean isUseCache (Request <?> Request) {return request. shouldCache () & mReqCache. get (request. getUrl ())! = Null;} public void quit () {isStop = true; interrupt ();}}
When you start a Request queue, we will start a specified number of networkexecutors (refer to the Request, Response, and Request queues for Android Network Framework ). When constructing NetworkExecutor, the request queue and HttpStack are injected, so that NetworkExecutor has two main elements: Request queue and HttpStack. Then, the request is continuously retrieved in the loop of the run function and handed to HttpStack for execution. In the meantime, it will determine whether the request needs to be cached and whether there is already a cache, if the cache is used and the cache already exists, the cached results are used. Execute the http request in the run function, so that the network request is executed in the Child thread. HttpStack is required for Http execution, but ResponseDelivery is required for distributing the results to the UI thread. Next we will introduce them one by one.


HttpStack

HttpStack is only an interface with only one receivmrequest function, that is, the execution request.

/*** Network request execution interface ** @ author mrsimple */public interface HttpStack {/*** execute Http request *** @ param request to be executed * @ return * /public Response initiate mrequest (Request <?> Request );}

HttpStack is the real executor of network requests, including HttpClientStack and HttpUrlConnStack. The two are HttpClient of Apache and HttpURLConnection of java respectively. For the differences between the two, see Android network access, use HttpURLConnection or HttpClient? By default, we will build the corresponding HttpStack Based on the api version. Of course, you can also implement an HttpStack by yourself and then pass it in through the SimpleNet factory function.

For example:

/*** @ Param coreNums thread cores * @ param httpStack http executor */protected RequestQueue (int coreNums, HttpStack httpStack) {mDispatcherNums = coreNums; mHttpStack = httpStack! = Null? HttpStack: HttpStackFactory. createHttpStack ();}
HttpStack is passed during the Purchase Request queue. If httpStack is empty, HttpStackFactory generates the corresponding HttpStack Based on the api version. That is, HttpClientStack is lower than api 9, and HttpUrlConnStack is later than api 9.
/*** Select HttpClient or HttpURLConnection according to the api version ** @ author mrsimple */public final class HttpStackFactory {private static final int GINGERBREAD_SDK_NUM = 9; /*** create different Http Actuators Based on the SDK version, that is, HttpClient is used before SDK 9, and HttlUrlConnection is used later. * for the differences between the two, see: * http://android-developers.blogspot.com/2011/09/androids-http-clients.html ** @ return */public static HttpStack createHttpStack () {int runtimeSDKApi = Build. VERSION. SDK_INT; if (runtimeSDKApi> = GINGERBREAD_SDK_NUM) {return new HttpUrlConnStack () ;}return new HttpClientStack ();}}

HttpClientStack and HttpUrlConnStack encapsulate http requests of HttpClient and HttpURLConnection, and construct requests, set headers, set request parameters, and parse Response. For this layer, we did not provide an abstract class because HttpClient and HttpURLConnection do not belong to the same class family and their behavior is similar, however, the types involved are different. Here we provide an example of HttpUrlConnStack, Which is busy recently. Therefore, the write configuration is relatively simple and can be optimized by users who need it.

/*** Use HttpURLConnection to execute the HttpStack ** @ author mrsimple */public class HttpUrlConnStack implements HttpStack {/*** configure Https */HttpUrlConnConfig mConfig = HttpUrlConnConfig. getConfig (); @ Override public Response initiate mrequest (Request <?> Request) {HttpURLConnection urlConnection = null; try {// construct HttpURLConnection urlConnection = createUrlConnection (request. getUrl (); // set headers setRequestHeaders (urlConnection, request); // set the Body parameter setRequestParams (urlConnection, request); // configure configHttps (request) for https ); return fetchResponse (urlConnection);} catch (Exception e) {e. printStackTrace ();} finally {if (urlConnection! = Null) {urlConnection. disconnect () ;}} return null;} private HttpURLConnection createUrlConnection (String url) throws IOException {URL newURL = new URL (url); URLConnection urlConnection = newURL. openConnection (); urlConnection. setConnectTimeout (mConfig. connTimeOut); urlConnection. setReadTimeout (mConfig. soTimeOut); urlConnection. setDoInput (true); urlConnection. setUseCaches (false); return (HttpU RLConnection) urlConnection;} private void configHttps (Request <?> Request) {if (request. isHttps () {SSLSocketFactory sslFactory = mConfig. getSslSocketFactory (); // configure https if (sslFactory! = Null) {HttpsURLConnection. setdefadefasslsocketfactory (sslFactory); HttpsURLConnection. Timeout (mConfig. getHostnameVerifier () ;}} private void setRequestHeaders (HttpURLConnection connection, Request <?> Request) {Set <String> headersKeys = request. getHeaders (). keySet (); for (String headerName: headersKeys) {connection. addRequestProperty (headerName, request. getHeaders (). get (headerName) ;}} protected void setRequestParams (HttpURLConnection connection, Request <?> Request) throws ProtocolException, IOException {HttpMethod 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 (Request. HEADER_CONTENT_TYPE, request. getBodyContentType (); // write params data to connection DataOutputStream dataOutputStream = new DataOutputStream (connection. getOutputStream (); dataOutputStream. write (body); dataOutputStream. close () ;}} private Response fetchResponse (HttpURLConnection c Onnection) throws IOException {// Initialize HttpResponse with data from the HttpURLConnection. protocolVersion protocolVersion = new ProtocolVersion ("HTTP", 1, 1); int responseCode = connection. getResponseCode (); if (responseCode =-1) {throw new IOException ("cocould not retrieve response code from HttpUrlConnection. ");} // status row data StatusLine responseStatus = new BasicStatusLine (protocolVersion, Connection. getResponseCode (), connection. getResponseMessage (); // construct response Response = new response (responseStatus); // set response data response. setEntity (entityFromURLConnwction (connection); addHeadersToResponse (response, connection); return response ;}/ *** get the data stream after executing the HTTP request, that is, the stream ** @ param connection * @ return */private HttpEntity entityFromURLConnwction (HttpURLConnection connec Tion) {BasicHttpEntity entity = new BasicHttpEntity (); InputStream inputStream = null; try {inputStream = connection. getInputStream ();} catch (IOException e) {e. printStackTrace (); inputStream = connection. getErrorStream ();} // TODO: GZIP entity. setContent (inputStream); entity. setContentLength (connection. getContentLength (); entity. setContentEncoding (connection. getContentEncoding (); entity. s EtContentType (connection. getContentType (); return entity;} private void addHeadersToResponse (BasicHttpResponse response, HttpURLConnection connection) {for (Entry <String, List <String> header: connection. getHeaderFields (). entrySet () {if (header. getKey ()! = Null) {Header h = new BasicHeader (header. getKey (), header. getValue (). get (0); response. addHeader (h );}}}}
The code is very simple.

ResponseDelivery

In the Response mrequest function of HttpStack, we will return a Response object containing the corresponding Response of our request. For more information about the Response class, refer to the Request, Response class and Request queue of the Android network framework. The final step of executing the http request in NetworkExecutor will distribute the result to the UI thread. The main task is to execute the request callback to the UI thread so that users can update the UI and other operations.

@ Override public void run () {try {while (! IsStop) {final Request <?> Request = mRequestQueue. take (); if (request. isCanceled () {Log. d ("####", "### canceled"); continue;} Response response = null; if (isUseCache (request )) {// obtain response = mReqCache from the cache. get (request. getUrl ();} else {// get data from the network response = mHttpStack. required mrequest (request); // if the request needs to be cached, the request is cached to mResponseCache if (request. shouldCache () & isSuccess (response) {mReqCache. put (request. getUrl (), response) ;}// the request result is mResponseDelivery. deliveryResponse (request, response) ;}} catch (InterruptedException e) {Log. I ("", "### request the distributor to exit ");}}
Whether obtained from the cache or from the network, we get a Response object. Finally, we use the ResponseDelivery object to distribute the results to the UI thread.

ResponseDelivery encapsulates the Handler associated with the UI thread message queue. In the deliveryResponse function, the request deliveryResponse is executed in the UI thread. Since we have a Handler object associated with the UI thread, we can directly construct a Runnable and execute the deliveryResponse function of the request in the Runnable. In the deliveryResponse of the Request class, parseResponse will be called to parse the Response result. The returned result type is T in the Request <T>, which is specified in the Request subclass, such as JsonRequest, then the returned Response result is JSONObject. In this way, we get the json data returned by the server and pass the json result to the UI thread through callback. You can update the UI in the callback.

This mainly involves abstract and generic types. In many cases, generic types are very important for writing frameworks. Therefore, familiarity with the use of abstract and generic types is an important step in object-oriented development.

The ResponseDelivery code is as follows:

/*** Request result shipping class, deliver the request result to the UI thread ** @ author mrsimple */class ResponseDelivery implements Executor {/*** hander */Handler mResponseHandler = new Handler (loler. getmainlogoff ();/*** process the request result and execute it in the UI thread ** @ param Request * @ param response */public void deliveryResponse (final request <?> Request, final Response response) {Runnable respRunnable = new Runnable () {@ Override public void run () {request. deliveryResponse (response) ;}}; execute (respRunnable) ;}@ Override public void execute (Runnable command) {mResponseHandler. post (command );}}

The deliveryResponse function of the Request class.

/*** Process Response. This method runs in the UI thread. ** @ param response */public final void deliveryResponse (Response response) {T result = parseResponse (response); if (mRequestListener! = Null) {int stCode = response! = Null? Response. getStatusCode ():-1; String msg = response! = Null? Response. getMessage (): "unkown error"; mRequestListener. onComplete (stCode, result, msg );}}

In this way, the entire request process is complete. Next we will summarize this process.

The data formats returned by servers of different users are inconsistent. Therefore, we define the Request <T> generic base class. Generic T is the type of returned data format. For example, if the returned data is in json format, the corresponding request is JsonRequest, the generic T is JSONObject, And the parseResponse function is overwritten in JsonRequest to convert the original data in the Response to JSONObject. Then, put the request in the queue, NetworkExecutor distributes the request to HttpStack for execution, obtains the Response object after the execution is complete, and finally ResponseDelivery delivers the result to the UI thread through the request callback.

Github Link

Https://github.com/bboyfeiyu/simple_net_framework



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.