教你写Android网络框架之Request、Response类与请求队列

来源:互联网 发布:威少11赛季数据 编辑:程序博客网 时间:2024/05/21 07:47

转载请注明出处,本文来自【 Mr.Simple的博客 】

我正在参加博客之星,点击这里投我一票吧,谢谢~   

前言

在教你写Android网络框架之基本架构一文中我们已经介绍了SimpleNet网络框架的基本结构,今天我们就开始从代码的角度来开始切入该网络框架的实现,在剖析的同时我们会分析设计思路,以及为什么要这样做,这样做的好处是什么。这样我们不仅学到了如何实现网络框架,也会学到设计一个通用的框架应该有哪些考虑,这就扩展到框架设计的范畴,通过这个简单的实例希望能给新人一些帮助。当然这只是一家之言,大家都可以有自己的实现思路。

正如你所看到的,这系列博客是为新人准备的,如果你是高手,请忽略。

在框架开发当中,很重要的一点就是抽象。也就是面向对象中重要的一条原则: 依赖倒置原则,简单来说就是要依赖抽象,而不依赖具体。这样就使得我们的框架具有可扩展性,同时也满足了开闭原则,即对扩展开放,对修改关闭。针对于我们的网络框架来说,最重要的抽象就是Reqeust类、Response类,因此今天我们就从两个类开始切入。最后我们再引入网络框架中的请求队列(RequestQueue),这是SimpleNet中的中枢神经,所有的请求都需要放到该队列,然后等待着被执行。请求队列就像工厂中的流水线一样,而网络请求就像流水线上的待加工的产品。执行网络请求的对象就类似工厂中的工人,在自己的岗位上等待流水线上传递过来的产品,然后对其加工,加工完就将产品放到其他的位置。它们角色对应关系参考图1,如对SimpleNet的一些角色不太清楚可参考教你写Android网络框架之基本架构一文。



Request类

既然网络框架,那么我们先从网络请求类开始。前文已经说过,既然是框架,那么就需要可扩展性。因此注定了Request是抽象,而不是具体。而对于网络请求来说,用户得到的请求结果格式是不确定,比如有的服务器返回的是json,有的返回的是xml,有的直接是字符串。但是对于Http Response来说,它的返回数据类型都是Stream,也就是我们得到的原始数据都是二进制的流。所以在Request基类中我们必须预留方法来解析Response返回的具体类型,虽然返回的类型不同,但是他们的处理逻辑是一样的,因此我们可把Request作为泛型类,它的泛型类型就是它的返回数据类型,比如Request<String>,那么它的返回数据类型就是String类型的。另外还有请求的优先级、可取消等,我们这里先给出核心代码,然后再继续分析。


  1. /** 
  2.  * 网络请求类. 注意GET和DELETE不能传递请求参数,因为其请求的性质所致,用户可以将参数构建到url后传递进来到Request中. 
  3.  *  
  4.  * @author mrsimple 
  5.  * @param <T> T为请求返回的数据类型 
  6.  */  
  7. public abstract class Request<T> implements Comparable<Request<T>> {  
  8.   
  9.     /** 
  10.      * http请求方法枚举,这里我们只有GET, POST, PUT, DELETE四种 
  11.      *  
  12.      * @author mrsimple 
  13.      */  
  14.     public static enum HttpMethod {  
  15.         GET("GET"),  
  16.         POST("POST"),  
  17.         PUT("PUT"),  
  18.         DELETE("DELETE");  
  19.   
  20.         /** http request type */  
  21.         private String mHttpMethod = "";  
  22.   
  23.         private HttpMethod(String method) {  
  24.             mHttpMethod = method;  
  25.         }  
  26.   
  27.         @Override  
  28.         public String toString() {  
  29.             return mHttpMethod;  
  30.         }  
  31.     }  
  32.   
  33.     /** 
  34.      * 优先级枚举 
  35.      *  
  36.      * @author mrsimple 
  37.      */  
  38.     public static enum Priority {  
  39.         LOW,  
  40.         NORMAL,  
  41.         HIGN,  
  42.         IMMEDIATE  
  43.     }  
  44.   
  45.     /** 
  46.      * Default encoding for POST or PUT parameters. See 
  47.      * {@link #getParamsEncoding()}. 
  48.      */  
  49.     private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";  
  50.     /** 
  51.      * 请求序列号 
  52.      */  
  53.     protected int mSerialNum = 0;  
  54.     /** 
  55.      * 优先级默认设置为Normal 
  56.      */  
  57.     protected Priority mPriority = Priority.NORMAL;  
  58.     /** 
  59.      * 是否取消该请求 
  60.      */  
  61.     protected boolean isCancel = false;  
  62.   
  63.     /** 该请求是否应该缓存 */  
  64.     private boolean mShouldCache = true;  
  65.     /** 
  66.      * 请求Listener 
  67.      */  
  68.     protected RequestListener<T> mRequestListener;  
  69.     /** 
  70.      * 请求的url 
  71.      */  
  72.     private String mUrl = "";  
  73.     /** 
  74.      * 请求的方法 
  75.      */  
  76.     HttpMethod mHttpMethod = HttpMethod.GET;  
  77.   
  78.     /** 
  79.      * 请求的header 
  80.      */  
  81.     private Map<String, String> mHeaders = new HashMap<String, String>();  
  82.     /** 
  83.      * 请求参数 
  84.      */  
  85.     private Map<String, String> mBodyParams = new HashMap<String, String>();  
  86.   
  87.     /** 
  88.      * @param method 
  89.      * @param url 
  90.      * @param listener 
  91.      */  
  92.     public Request(HttpMethod method, String url, RequestListener<T> listener) {  
  93.         mHttpMethod = method;  
  94.         mUrl = url;  
  95.         mRequestListener = listener;  
  96.     }  
  97.   
  98.     /** 
  99.      * 从原生的网络请求中解析结果,子类覆写 
  100.      *  
  101.      * @param response 
  102.      * @return 
  103.      */  
  104.     public abstract T parseResponse(Response response);  
  105.   
  106.     /** 
  107.      * 处理Response,该方法运行在UI线程. 
  108.      *  
  109.      * @param response 
  110.      */  
  111.     public final void deliveryResponse(Response response) {  
  112.         // 解析得到请求结果  
  113.         T result = parseResponse(response);  
  114.         if (mRequestListener != null) {  
  115.             int stCode = response != null ? response.getStatusCode() : -1;  
  116.             String msg = response != null ? response.getMessage() : "unkown error";  
  117.             mRequestListener.onComplete(stCode, result, msg);  
  118.         }  
  119.     }  
  120.   
  121.     public String getUrl() {  
  122.         return mUrl;  
  123.     }  
  124.   
  125.   
  126.   
  127.     public int getSerialNumber() {  
  128.         return mSerialNum;  
  129.     }  
  130.   
  131.     public void setSerialNumber(int mSerialNum) {  
  132.         this.mSerialNum = mSerialNum;  
  133.     }  
  134.   
  135.     public Priority getPriority() {  
  136.         return mPriority;  
  137.     }  
  138.   
  139.     public void setPriority(Priority mPriority) {  
  140.         this.mPriority = mPriority;  
  141.     }  
  142.   
  143.     protected String getParamsEncoding() {  
  144.         return DEFAULT_PARAMS_ENCODING;  
  145.     }  
  146.   
  147.     public String getBodyContentType() {  
  148.         return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();  
  149.     }  
  150.   
  151.     public HttpMethod getHttpMethod() {  
  152.         return mHttpMethod;  
  153.     }  
  154.   
  155.     public Map<String, String> getHeaders() {  
  156.         return mHeaders;  
  157.     }  
  158.   
  159.     public Map<String, String> getParams() {  
  160.         return mBodyParams;  
  161.     }  
  162.   
  163.   
  164.     public void cancel() {  
  165.         isCancel = true;  
  166.     }  
  167.   
  168.     public boolean isCanceled() {  
  169.         return isCancel;  
  170.     }  
  171.   
  172.     /** 
  173.      * 返回POST或者PUT请求时的Body参数字节数组 
  174.      * 
  175.      */  
  176.     public byte[] getBody() {  
  177.         Map<String, String> params = getParams();  
  178.         if (params != null && params.size() > 0) {  
  179.             return encodeParameters(params, getParamsEncoding());  
  180.         }  
  181.         return null;  
  182.     }  
  183.   
  184.     /** 
  185.      * 将参数转换为Url编码的参数串 
  186.      */  
  187.     private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {  
  188.         StringBuilder encodedParams = new StringBuilder();  
  189.         try {  
  190.             for (Map.Entry<String, String> entry : params.entrySet()) {  
  191.                 encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));  
  192.                 encodedParams.append('=');  
  193.                 encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));  
  194.                 encodedParams.append('&');  
  195.             }  
  196.             return encodedParams.toString().getBytes(paramsEncoding);  
  197.         } catch (UnsupportedEncodingException uee) {  
  198.             throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);  
  199.         }  
  200.     }  
  201.   
  202.     // 用于对请求的排序处理,根据优先级和加入到队列的序号进行排序  
  203.     @Override  
  204.     public int compareTo(Request<T> another) {  
  205.         Priority myPriority = this.getPriority();  
  206.         Priority anotherPriority = another.getPriority();  
  207.         // 如果优先级相等,那么按照添加到队列的序列号顺序来执行  
  208.         return myPriority.equals(another) ? this.getSerialNumber() - another.getSerialNumber()  
  209.                 : myPriority.ordinal() - anotherPriority.ordinal();  
  210.     }  
  211.   
  212.   
  213.   
  214.     /** 
  215.      * 网络请求Listener,会被执行在UI线程 
  216.      *  
  217.      * @author mrsimple 
  218.      * @param <T> 请求的response类型 
  219.      */  
  220.     public static interface RequestListener<T> {  
  221.         /** 
  222.          * 请求完成的回调 
  223.          *  
  224.          * @param response 
  225.          */  
  226.         public void onComplete(int stCode, T response, String errMsg);  
  227.     }  
  228. }  

上述代码Request<T>为抽象类,T则为该请求Response的数据格式。这个T是请求类中的一个比较重要的点,不同的人有不同的需求,即请求Reponse的数据格式并不是都是一样的,我们必须考虑到请求返回类型的多样性,用泛型T来表示返回的数据格式类型,然后Request子类覆写对应的方法实现解析Response的数据格式,最后调用请求Listener将请求结果执行在UI线程,这样整个请求就完成了。

每个Request都有一个序列号,该序列号由请求队列生成,标识该请求在队列中的序号,该序号和请求优先级决定了该请求在队列中的排序,即它在请求队列的执行顺序。每个请求有请求方式,例如"POST"、"GET",这里我们用枚举来代替,具名类型比单纯的字符串更易于使用。每个Request都可以添加Header、Body参数 ( 关于请求参数的格式可以参考 四种常见的 POST 提交数据方式),并且可以取消。抽象类封装了通用的代码,只有可变的部分是抽象函数,这里只有parseResponse这个函数。

例如,我们返回的数据格式是Json,那么我们构建一个子类叫做JsonRequest,示例代码如下。

  1. /** 
  2.  * 返回的数据类型为Json的请求, Json对应的对象类型为JSONObject 
  3.  *  
  4.  * @author mrsimple 
  5.  */  
  6. public class JsonRequest extends Request<JSONObject> {  
  7.   
  8.     public JsonRequest(HttpMethod method, String url, RequestListener<JSONObject> listener) {  
  9.         super(method, url, listener);  
  10.     }  
  11.   
  12.       
  13.     /** 
  14.      * 将Response的结果转换为JSONObject 
  15.      */  
  16.     @Override  
  17.     public JSONObject parseResponse(Response response) {  
  18.         String jsonString = new String(response.getRawData());  
  19.         try {  
  20.             return new JSONObject(jsonString);  
  21.         } catch (JSONException e) {  
  22.             e.printStackTrace();  
  23.         }  
  24.         return null;  
  25.     }  
  26. }  

可以看到,实现一个请求类还是非常简单的,只需要覆写parseResponse函数来解析你的请求返回的数据即可。这样就保证了可扩展性,比如后面如果我想使用这个框架来做一个ImageLoader,那么我可以创建一个ImageRequest,该请求返回的类型就是Bitmap,那么我们只需要覆写parseResponse函数,然后把结果转换成Bitmap即可。

这里引入了Response类,这个Response类存储了请求的状态码、请求结果等内容,我们继续往下看。


Response类

每个请求都对应一个Response,但这里的问题是这个Response的数据格式我们是不知道的。我们写的是框架,不是应用。框架只是构建一个基本环境,并且附带一些比较常用的类,比如这里的JsonRequest。但是重要的一点是可以让用户自由、简单的扩展以实现他的需求。对于Response类来说,我们最重要的一点就是要确定请求结果的数据格式类型。我们都知道,HTTP实际上是基于TCP协议,而TCP协议又是基于Socket,Socket实际上操作的也就是输入、输出流,输出流是向服务器写数据,输入流自然是从服务器读取数据。因此我们在Response类中应该使用InputStream存储结果或者使用更为易于使用的字节数组,这里我们使用字节数组来存储。我们来看Response类。


  1. /** 
  2.  * 请求结果类,继承自BasicHttpResponse,将结果存储在rawData中. 
  3.  * @author mrsimple 
  4.  */  
  5. public class Response extends BasicHttpResponse {  
  6.   
  7.     public byte[] rawData = new byte[0];  
  8.   
  9.     public Response(StatusLine statusLine) {  
  10.         super(statusLine);  
  11.     }  
  12.   
  13.     public Response(ProtocolVersion ver, int code, String reason) {  
  14.         super(ver, code, reason);  
  15.     }  
  16.   
  17.     @Override  
  18.     public void setEntity(HttpEntity entity) {  
  19.         super.setEntity(entity);  
  20.         rawData = entityToBytes(getEntity());  
  21.     }  
  22.   
  23.     public byte[] getRawData() {  
  24.         return rawData;  
  25.     }  
  26.   
  27.     public int getStatusCode() {  
  28.         return getStatusLine().getStatusCode();  
  29.     }  
  30.   
  31.     public String getMessage() {  
  32.         return getStatusLine().getReasonPhrase();  
  33.     }  
  34.   
  35.     /** Reads the contents of HttpEntity into a byte[]. */  
  36.     private byte[] entityToBytes(HttpEntity entity) {  
  37.         try {  
  38.             return EntityUtils.toByteArray(entity);  
  39.         } catch (IOException e) {  
  40.             e.printStackTrace();  
  41.         }  
  42.         return new byte[0];  
  43.     }  
  44.   
  45. }  

这个类很简单,只是继承了BasicHttpResponse,然后将输入流转换成字节数组,然后包装了几个常用的方法,主要是为了使用简单吧。我们将结果存储为字节数组,这样可以用户可以很方便的将结果转换为String、bitmap等数据类型,如果直接存储的是InputStream,那么在很多时候用户需要在外围将InputStream先转换为字节数组,然后再转换为最终的格式,例如InputStream转为String类型。这也是为什么我们这里选用byte[]而不用InputStream的原因。

请求队列


网络请求队列也比较简单,实际上就是内部封装了一个优先级队列,在构建队列时会启动几个NetworkExecutor ( 子线程 )来从请求队列中获取请求,并且执行请求。请求队列会根据请求的优先级进行排序,这样就保证了一些优先级高的请求得到尽快的处理,这也就是为什么Request类中实现了Comparable接口的原因。如果优先级一致的情况下,则会根据请求加入到队列的顺序来排序,这个序号由请求队列生成,这样就保证了优先级一样的情况下按照FIFO的策略执行。

  1. /** 
  2.  * 请求队列, 使用优先队列,使得请求可以按照优先级进行处理. [ Thread Safe ] 
  3.  *  
  4.  * @author mrsimple 
  5.  */  
  6. public final class RequestQueue {  
  7.     /** 
  8.      * 请求队列 [ Thread-safe ] 
  9.      */  
  10.     private BlockingQueue<Request<?>> mRequestQueue = new PriorityBlockingQueue<Request<?>>();  
  11.     /** 
  12.      * 请求的序列化生成器 
  13.      */  
  14.     private AtomicInteger mSerialNumGenerator = new AtomicInteger(0);  
  15.   
  16.     /** 
  17.      * 默认的核心数 
  18.      */  
  19.     public static int DEFAULT_CORE_NUMS = Runtime.getRuntime().availableProcessors() + 1;  
  20.     /** 
  21.      * CPU核心数 + 1个分发线程数 
  22.      */  
  23.     private int mDispatcherNums = DEFAULT_CORE_NUMS;  
  24.     /** 
  25.      * NetworkExecutor,执行网络请求的线程 
  26.      */  
  27.     private NetworkExecutor[] mDispatchers = null;  
  28.     /** 
  29.      * Http请求的真正执行者 
  30.      */  
  31.     private HttpStack mHttpStack;  
  32.   
  33.     /** 
  34.      * @param coreNums 线程核心数 
  35.      */  
  36.     protected RequestQueue(int coreNums, HttpStack httpStack) {  
  37.         mDispatcherNums = coreNums;  
  38.         mHttpStack = httpStack != null ? httpStack : HttpStackFactory.createHttpStack();  
  39.     }  
  40.   
  41.     /** 
  42.      * 启动NetworkExecutor 
  43.      */  
  44.     private final void startNetworkExecutors() {  
  45.         mDispatchers = new NetworkExecutor[mDispatcherNums];  
  46.         for (int i = 0; i < mDispatcherNums; i++) {  
  47.             mDispatchers[i] = new NetworkExecutor(mRequestQueue, mHttpStack);  
  48.             mDispatchers[i].start();  
  49.         }  
  50.     }  
  51.   
  52.     public void start() {  
  53.         stop();  
  54.         startNetworkExecutors();  
  55.     }  
  56.   
  57.     /** 
  58.      * 停止NetworkExecutor 
  59.      */  
  60.     public void stop() {  
  61.         if (mDispatchers != null && mDispatchers.length > 0) {  
  62.             for (int i = 0; i < mDispatchers.length; i++) {  
  63.                 mDispatchers[i].quit();  
  64.             }  
  65.         }  
  66.     }  
  67.   
  68.     /** 
  69.      * 不能重复添加请求 
  70.      *  
  71.      * @param request 
  72.      */  
  73.     public void addRequest(Request<?> request) {  
  74.         if (!mRequestQueue.contains(request)) {  
  75.             request.setSerialNumber(this.generateSerialNumber());  
  76.             mRequestQueue.add(request);  
  77.         } else {  
  78.             Log.d("""### 请求队列中已经含有");  
  79.         }  
  80.     }  
  81.   
  82.     public void clear() {  
  83.         mRequestQueue.clear();  
  84.     }  
  85.   
  86.     public BlockingQueue<Request<?>> getAllRequests() {  
  87.         return mRequestQueue;  
  88.     }  
  89.   
  90.     /** 
  91.      * 为每个请求生成一个系列号 
  92.      *  
  93.      * @return 序列号 
  94.      */  
  95.     private int generateSerialNumber() {  
  96.         return mSerialNumGenerator.incrementAndGet();  
  97.     }  
  98. }  

这里引入了一个HttpStack,这是一个接口,只有一个函数。该接口定义了执行网络请求的抽象,代码如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * 执行网络请求的接口 
  3.  *  
  4.  * @author mrsimple 
  5.  */  
  6. public interface HttpStack {  
  7.     /** 
  8.      * 执行Http请求 
  9.      *  
  10.      * @param request 待执行的请求 
  11.      * @return 
  12.      */  
  13.     public Response performRequest(Request<?> request);  
  14. }  

今天就先到这里吧,关于HttpStack、NetworkExecutor、ResponseDelivery的介绍将在下一篇博客中更新,敬请期待。

如果你看到这里都不给我投一篇,那简直太不够意思了!!点击这里投我一票吧,谢谢~   

Github地址

SimpleNet网络框架地址


0 0
原创粉丝点击