程序员人生 网站导航

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

栏目:综合技术时间:2015-01-30 08:14:59

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

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

前言

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

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

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

图1


Request类

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

/** * 网络要求类. 注意GET和DELETE不能传递要求参数,由于其要求的性质而至,用户可以将参数构建到url后传递进来到Request中. * * @author mrsimple * @param <T> T为要求返回的数据类型 */ public abstract class Request<T> implements Comparable<Request<T>> { /** * http要求方法枚举,这里我们只有GET, POST, PUT, DELETE4种 * * @author mrsimple */ public static enum HttpMethod { GET("GET"), POST("POST"), PUT("PUT"), DELETE("DELETE"); /** http request type */ private String mHttpMethod = ""; private HttpMethod(String method) { mHttpMethod = method; } @Override public String toString() { return mHttpMethod; } } /** * 优先级枚举 * * @author mrsimple */ public static enum Priority { LOW, NORMAL, HIGN, IMMEDIATE } /** * Default encoding for POST or PUT parameters. See * {@link #getParamsEncoding()}. */ private static final String DEFAULT_PARAMS_ENCODING = "UTF⑻"; /** * 要求序列号 */ protected int mSerialNum = 0; /** * 优先级默许设置为Normal */ protected Priority mPriority = Priority.NORMAL; /** * 是不是取消该要求 */ protected boolean isCancel = false; /** 该要求是不是应当缓存 */ private boolean mShouldCache = true; /** * 要求Listener */ protected RequestListener<T> mRequestListener; /** * 要求的url */ private String mUrl = ""; /** * 要求的方法 */ HttpMethod mHttpMethod = HttpMethod.GET; /** * 要求的header */ private Map<String, String> mHeaders = new HashMap<String, String>(); /** * 要求参数 */ private Map<String, String> mBodyParams = new HashMap<String, String>(); /** * @param method * @param url * @param listener */ public Request(HttpMethod method, String url, RequestListener<T> listener) { mHttpMethod = method; mUrl = url; mRequestListener = listener; } /** * 从原生的网络要求中解析结果,子类覆写 * * @param response * @return */ public abstract T parseResponse(Response response); /** * 处理Response,该方法运行在UI线程. * * @param response */ public final void deliveryResponse(Response response) { // 解析得到要求结果 T result = parseResponse(response); if (mRequestListener != null) { int stCode = response != null ? response.getStatusCode() : ⑴; String msg = response != null ? response.getMessage() : "unkown error"; mRequestListener.onComplete(stCode, result, msg); } } public String getUrl() { return mUrl; } public int getSerialNumber() { return mSerialNum; } public void setSerialNumber(int mSerialNum) { this.mSerialNum = mSerialNum; } public Priority getPriority() { return mPriority; } public void setPriority(Priority mPriority) { this.mPriority = mPriority; } protected String getParamsEncoding() { return DEFAULT_PARAMS_ENCODING; } public String getBodyContentType() { return "application/x-www-form-urlencoded; charset=" + getParamsEncoding(); } public HttpMethod getHttpMethod() { return mHttpMethod; } public Map<String, String> getHeaders() { return mHeaders; } public Map<String, String> getParams() { return mBodyParams; } public void cancel() { isCancel = true; } public boolean isCanceled() { return isCancel; } /** * 返回POST或PUT要求时的Body参数字节数组 * */ public byte[] getBody() { Map<String, String> params = getParams(); if (params != null && params.size() > 0) { return encodeParameters(params, getParamsEncoding()); } return null; } /** * 将参数转换为Url编码的参数串 */ 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); } } // 用于对要求的排序处理,根据优先级和加入到队列的序号进行排序 @Override public int compareTo(Request<T> another) { Priority myPriority = this.getPriority(); Priority anotherPriority = another.getPriority(); // 如果优先级相等,那末依照添加到队列的序列号顺序来履行 return myPriority.equals(another) ? this.getSerialNumber() - another.getSerialNumber() : myPriority.ordinal() - anotherPriority.ordinal(); } /** * 网络要求Listener,会被履行在UI线程 * * @author mrsimple * @param <T> 要求的response类型 */ public static interface RequestListener<T> { /** * 要求完成的回调 * * @param response */ public void onComplete(int stCode, T response, String errMsg); } }

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

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

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

/** * 返回的数据类型为Json的要求, Json对应的对象类型为JSONObject * * @author mrsimple */ public class JsonRequest extends Request<JSONObject> { public JsonRequest(HttpMethod method, String url, RequestListener<JSONObject> listener) { super(method, url, listener); } /** * 将Response的结果转换为JSONObject */ @Override public JSONObject parseResponse(Response response) { String jsonString = new String(response.getRawData()); try { return new JSONObject(jsonString); } catch (JSONException e) { e.printStackTrace(); } return null; } }

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

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

Response类

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

/** * 要求结果类,继承自BasicHttpResponse,将结果存储在rawData中. * @author mrsimple */ public class Response extends BasicHttpResponse { public byte[] rawData = new byte[0]; public Response(StatusLine statusLine) { super(statusLine); } public Response(ProtocolVersion ver, int code, String reason) { super(ver, code, reason); } @Override public void setEntity(HttpEntity entity) { super.setEntity(entity); rawData = entityToBytes(getEntity()); } public byte[] getRawData() { return rawData; } public int getStatusCode() { return getStatusLine().getStatusCode(); } public String getMessage() { return getStatusLine().getReasonPhrase(); } /** Reads the contents of HttpEntity into a byte[]. */ private byte[] entityToBytes(HttpEntity entity) { try { return EntityUtils.toByteArray(entity); } catch (IOException e) { e.printStackTrace(); } return new byte[0]; } }

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


要求队列

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

/** * 要求队列, 使用优先队列,使得要求可以依照优先级进行处理. [ Thread Safe ] * * @author mrsimple */ public final class RequestQueue { /** * 要求队列 [ Thread-safe ] */ private BlockingQueue<Request<?>> mRequestQueue = new PriorityBlockingQueue<Request<?>>(); /** * 要求的序列化生成器 */ private AtomicInteger mSerialNumGenerator = new AtomicInteger(0); /** * 默许的核心数 */ public static int DEFAULT_CORE_NUMS = Runtime.getRuntime().availableProcessors() + 1; /** * CPU核心数 + 1个分发线程数 */ private int mDispatcherNums = DEFAULT_CORE_NUMS; /** * NetworkExecutor,履行网络要求的线程 */ private NetworkExecutor[] mDispatchers = null; /** * Http要求的真正履行者 */ private HttpStack mHttpStack; /** * @param coreNums 线程核心数 */ protected RequestQueue(int coreNums, HttpStack httpStack) { mDispatcherNums = coreNums; mHttpStack = httpStack != null ? httpStack : HttpStackFactory.createHttpStack(); } /** * 启动NetworkExecutor */ private final void startNetworkExecutors() { mDispatchers = new NetworkExecutor[mDispatcherNums]; for (int i = 0; i < mDispatcherNums; i++) { mDispatchers[i] = new NetworkExecutor(mRequestQueue, mHttpStack); mDispatchers[i].start(); } } public void start() { stop(); startNetworkExecutors(); } /** * 停止NetworkExecutor */ public void stop() { if (mDispatchers != null && mDispatchers.length > 0) { for (int i = 0; i < mDispatchers.length; i++) { mDispatchers[i].quit(); } } } /** * 不能重复添加要求 * * @param request */ public void addRequest(Request<?> request) { if (!mRequestQueue.contains(request)) { request.setSerialNumber(this.generateSerialNumber()); mRequestQueue.add(request); } else { Log.d("", "### 要求队列中已含有"); } } public void clear() { mRequestQueue.clear(); } public BlockingQueue<Request<?>> getAllRequests() { return mRequestQueue; } /** * 为每一个要求生成1个系列号 * * @return 序列号 */ private int generateSerialNumber() { return mSerialNumGenerator.incrementAndGet(); } }

这里引入了1个HttpStack,这是1个接口,只有1个函数。该接口定义了履行网络要求的抽象,代码以下:

/** * 履行网络要求的接口 * * @author mrsimple */ public interface HttpStack { /** * 履行Http要求 * * @param request 待履行的要求 * @return */ public Response performRequest(Request<?> request); }

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

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

Github地址

SimpleNet网络框架地址

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐