《android开发进阶从小工到专家》读书笔记--网络框架的设计与实现

第一步:

《android开发进阶从小工到专家》读书笔记--网络框架的设计与实现

第一层:Request--请求类型,JSON,字符串,文件

第二层:消息队列--维护了提交给网络框架的请求列表,并且根据响应的规则进行排序。默认情况下按照优先级和进入队列的顺序来执行,该队列使用的是线程安全的PriorityBlockingQueue<E>,因为我们的队列会被并发访问,因此需要保证队列访问的原子性

第三层:NetworkExecutor--网络执行者,该Eexcutor继承自Thread,在run方法中循环访问请求队列,从请求队列中获取并执行HTTP请求,请求完成之后将结果投递给UI线程

第四层:Response以及Response投递类,使用ResponseDelivery来封装Response的投递,保证Response执行在UI线程。而Response会根据用户的不同需求将返回结果格式化为特定的类型。

第二步:网络请求类

public static enum HttpMethod{
GET("GET"),
POST("POST"),
PUT("PUT"),
DELETE("DELETE"); /** http request type */
private String mHttpMethod = ""; privte HttpMethod(String method){
mHttpMethod = method;
} @Override
public String toString(){
return mHttpMethod;
}
} //优先级枚举
public static enum Priority{
LOW,
NORMAL,
HIGH,
IMMEDIATE
}
/**
* 网络请求类,注意GET和DELETE不能传递请求参数,因为其请求的性质所致,用户可以将参数构建到URL后传递进来并到Request中
* @param <T> T为请求返回的数据类型
*/
public abstract class Request<T> implements Comparable<Request<T>>{
//默认的编码方式
private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";
//请求序列号
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 请求的目标URL
* @param listener 请求回调,将结果回调给用户
*/
public Request(HttpMethod method,String url,RequestListener<T> listener){
mHttpMethod = method;
mUrl = url;
mRequestlistener = listener;
} //从原生的网络请求中解析结果,子类必须覆写
public abstract T parseResponse(Response response); //处理Response,该方法需要运行在UI线程
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():"unknown error";
mRequestlistener.onComplete(stCode,result,msg);
}
}
//代码省略 protected String getParamsEncoding(){
return DEFAULT_PARAMS_ENCODING;
} public String getBodyContentType(){
return "application/x-www-form-urlencoded;charset="+getParamsEncoding();
} //返回POST或者PUT请求时的Body参数字节数组
public byte[] getBody(){
Map<String,String> params = getParams();
if(params!=null && params.size()>0){
return encodeParameters(params,getParamsEncoding());
}
return null;
} //将参数转换为URL编码的参数串,格式为key1=value1&key2=value2
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(UnsupportEncodingException 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线程
*@param <T> 请求的response类型
*/
public static interface RequestListener<T>{
//请求完成的回调
public void onComplete(int stCode,T response,String errMsg);
}
}

HTTP实际上是基于TCP协议,而TCP协议又是基于Socket,Socket实际上操作的也就是输入、输出流,输出流是向服务器写数据,输入流是从服务器读取数据。

第三步:响应类

//请求结果类,继承自BasicHttpResponse,将结果存储在rawData中
public class Response extends BasicHttpResponse{
//原始的Response主体数据
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;
} //代码省略
/**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];
}
}

第四步:请求队列

网络请求队列就是在内部封装了一个优先级队列,在构建队列时会启动指定个数的NetworkExecutor(继承自Thread)来从请求队列中获取、执行请求,请求队列会根据请求的优先级、序列号对所有Request进行排序。

//请求队列,使用优先队列,使得请求可以按照优先级进行处理
public final class RequsetQueue{
//线程安全的请求队列
private BlockingQueue<Request<?>> mRequestQueue = new PriorityBlockingQueue<Request<?>>();
//请求的序列化生成器
private AtomicInteger mSerialNumGenerator = new AtomicInteger(0);
//默认的核心数 为CPU格式加1
private int mDispatcherNums = DEFAULT_CORE_NUMS;
//NetworkExecutor,执行网络请求的线程
private NetworkExecutor[] mDispatchers = null;
//Http请求的真正执行者
private HttpStack mHttpStack; protected RequestQueue(int coreNums,HttpStack httpStack){
mDispatcherNums = coreNums; mHttpStack = httpStack !=null?httpStack:HttpStackFactory.createHttpStack();
} //启动NetworkExecutor
private final void startNetworkExecutors(){
mDispatchers = new NetworkExecutor[mDispatchNums];
for(int i=0;i<mDispatchNums;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();
}
}
} //添加请求到队列中
public void addRequest(Request<?> request){
if(!mRequestQueue.contains(request)){
//为请求设置序列号
request.setSerialNumber(this.generateSerialNumber());
mRequestQueue.add(request);
}else{
Log.d("","### 请求队列中已经含有");
}
} //代码省略
//为每个请求生成一个系列号
private int generateSerialNumber(){
return mSerialNumGenerator.incrementAndGet();
}
}

第五步:NetworkExecutor网络执行器

多个NetworkExecutor共享一个消息队列,在各个NetworkExecutor的run函数中循环地取请求队列中的请求,拿到一个请求之后通过HttpStack对象来真正地执行请求,最终将请求结果通过ResponseDelivery分发给UI线程。

NetworkExecutor实质上是一个Thread,在run方法中我们会执行一个循环,不断地从请求队列中取得请求,然后交给HttpStack。

//网络请求Exector,继承自Thread,从网络请求队列中循环读取请求并且执行
final class NetworkExecutor extends Thread{
//网络请求队列
private BlockingQueue<Request<?>> mRequestQueue;
//网络请求栈
private HttpStack mHttpStack;
//结果分发器,将结果投递到主线程
private static ResponseDelivery mResponseDelivery = new ResponseDelivery();
//请求缓存
private static Cache<String,Response> mReqCache = new LruMemCache();
//是否停止
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("###","### 取消执行了");
continue;
}
Response response = null;
if(isUseCache(request)){
//从缓存中取
response = mReqCache.get(request.getUrl());
}else{
//从网络中获取数据
response = mHttpCache.get(request.getUrl());
}else{
//从网络上获取数据
response = mHttpStack.performRequest(request);
//如果该请求需要缓存,那么请求成功则缓存到mResponseCache中
if(request.shouldCache() && isSuccess(response)){
mReqCache.put(request.getUrl(),response);
}
}
//分发请求结果
mResponseDelivery.deliveryResponse(request,response);
}
}catch(InterruptedExecption e){
Log.i("","### 请求分发器退出");
}
} private boolean isSuccess(Response response){
return reponse!=null && response.getStatusCode()==200;
} private boolean isUseCache(Request<?> request){
return request.shouldCache() && mReqCache.get(request.getUrl())!=null;
} public void quit(){
isStop = true;
interrupt();
}
}

第六步:执行网络请求的接口--HttpStack

//执行网络请求的接口
public interface HttpStack{
/**
* 执行HTTP请求
* @param request 待执行的请求
* @retrun 返回Response
*/
public Response performRequest(Request<?> request);
}
//使用HttpURLConnection执行网络请求的HttpStack
public class HttpUrlConnStack implements HttpStack{
@Override
public Response performRequest(Request<?> request){
HttpURLConnection urlConnection = null;
try{
//构建HttpURLConnection
urlConnection = createUrlConnection(request.getUrl());
//设置headers
setRequestHeaders(urlConnection,request);
//设置Body参数
setRequestParams(urlConnection,request);
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 (HttpURLConnection)urlConnection;
} 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 connection) 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("Could not retrieve response code from HttpURLConnection.");
}
//状态行数据
StatusLine responseStatus = new BasicStatusLine(ptocolVersion,connection.getResponseCode(),connection.getResponseMessage());
//构建response
Response response = new Response(responseStatus);
//设置response数据
response.setEntity(entityFromURLConnection(connection));
addHeadersToResponse(response,connection);
return response;
} /**
* 执行HTTP请求之后获取到其数据流,即返回请求结果的流
* @param connection
* @return
*/
private HttpEntity entityFromURLConnection(HttpURLConnection connection){
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.setContentType(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);
}
}
}
}

简单来说:构建HttpURLConnection,并通过HttpURLConnection对象设置请求Header、参数,然后发起请求,请求完成只有解析结果,并返回Response。

第七步:将请求的回调执行到UI线程--ResponseDelivery

//请求结果投递类,将请求结果投递给UI线程
class ResponseDelivery implements Executor{
//关联主线程消息队列的hander
Hander mResponseHandler = new Handler(Looper.getMainLooper());
/**
* 处理请求结果,将其执行在UI线程
* @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);
}
}

ResponseDelivery其实就是封装了关联了UI线程消息队列的Handler。

使用:

//1.构建并启动请求队列
RequestQueue mQueue = SimpleNet.newRequestQueue();
/**
* 发送GET请求,返回的是String类型的数据,同理还有{@see JsonRequest}、{@see MultipartRequest}
*/
private void sendStringRequest(){
//2.构建请求
StringRequest request = new StringRequest(HttpMethod.GET,"http://www.baidu.com",
new RequestListener<String>(){
@Override
public void onComplete(int stCode,String response,String errMsg){
//处理结果
}
});
//3.将请求添加到请求队列中
mQueue.addRequest(request);
}
上一篇:SQL 存储过程加事务的使用


下一篇:VC 获取指定文件夹路径的方法小结