自己动手写HTTP框架:异步任务篇
来源:互联网 发布:软件体系有哪些 编辑:程序博客网 时间:2024/05/16 15:01
需求
2011年的某一天,你的公司初进移动开发领域,上司要求你实现一套http网络请求框架,要求易扩展。
2011年!!!volley、imageloader、fresco、glide…..都是啥?????
需求分析
- 支持http协议get、post、put、delete
- 异步请求
- 请求错误统一处理
- 支持多文件上传
- 预处理服务器返回的数据
- 上传/下载 进度更新
- 取消请求
- 可扩展需求:
- timeout重试机制
- 多任务队列
- 关联activity
- 缓存刷新机制
系统分析与设计
核心模块分析(编码)
callback
为支持多种请求的扩张,这里采用抽象类。
ICallBack
public interface ICallBack { void onFailure(Exception result); void onSuccess(Object result); Object handle(HttpResponse response, IProgressListener iProgressListener); void onProgressUpdate(int curPos, int contentLength); /** * 在子线程中对返回值做预处理,比如保存到数据库等等操作(预处理返回的对象) * 如果不需要什么处理的话,什么都不需要做 * @param object * @return */ Object onPreHandle(Object object); Object onPresRequest();}
AbstractCallback
public abstract class AbstractCallback implements ICallBack{ public String path; private static final int IO_BUFFER_SIZE = 4 * 1024; // 做解析 @Override public Object handle(HttpResponse response, IProgressListener iProgressListener) { // file,json,xml,image,string try { HttpEntity entity = response.getEntity(); switch (response.getStatusLine().getStatusCode()) { // 返回时200的时候,我就去解析数据 case HttpStatus.SC_OK: // 文件,把服务器的返回直接写到文件里面 if (TextUtil.isValidate(path)) { FileOutputStream fos = new FileOutputStream(path); InputStream in = null; if (entity.getContentEncoding() != null) { String encoding = entity.getContentEncoding().getValue(); if (encoding != null && "gzip".equalsIgnoreCase(encoding)) { in = new GZIPInputStream(entity.getContent()); } else if (encoding != null && "deflate".equalsIgnoreCase(encoding)) { in = new InflaterInputStream(entity.getContent()); } } else { in = entity.getContent(); } byte[] b = new byte[IO_BUFFER_SIZE]; int read; long curPos = 0;// long length = entity.getContentLength(); while ((read = in.read(b)) != -1) { // update progress curPos += read; iProgressListener.onProgressUpdate((int) (curPos / 1024), (int) (entity.getContentLength())); fos.write(b, 0, read); } fos.flush(); fos.close(); in.close(); return bindData(path); } else { return bindData(EntityUtils.toString(entity)); } default: break; } } catch (ParseException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } protected Object bindData(String content) { return content; } public AbstractCallback setPath(String path) { this.path = path; return this; }}
StringCallBack
public abstract class StringCallBack extends AbstractCallback{ @Override protected Object bindData(String content) { if (TextUtil.isValidate(path)) { return FileUtil.readFromFile(path); } return content; }}
Request
public class Request { public enum RequestMethod { GET, POST, DELETE, PUT } public RequestMethod method; public String url; public String postContent; public Map<String, String> headers; public HttpEntity entity; public static final String ENCODING = "UTF-8"; public ICallBack mCallback; public Request(String url, RequestMethod method) { this.url = url; this.method = method; } // 表单形式 public void setEntity(ArrayList<NameValuePair> forms) { try { entity = new UrlEncodedFormEntity(forms, ENCODING); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } // string public void setEntity(String postContent) { try { entity = new StringEntity(postContent, ENCODING); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } // array public void setEntity(byte[] bytes) { entity = new ByteArrayEntity(bytes); } public void setCallback(ICallBack callback) { mCallback = callback; } public void execute() { RequestTask task = new RequestTask(this); task.execute(); }}
RequestTask
public class RequestTask extends AsyncTask<Object, Integer, Object> { private Request mRequest; public RequestTask(Request request) { mRequest = request; } @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Object doInBackground(Object... params) { try { Object object1 = mRequest.mCallback.onPresRequest(); if (object1 != null) { return object1; } HttpResponse response = HttpClientUtil.execute(mRequest); Object object = mRequest.mCallback.handle(response, new IProgressListener() { @Override public void onProgressUpdate(int curPos, int contentLength) { publishProgress(curPos, contentLength); } }); return mRequest.mCallback.onPreHandle(object); } catch (IOException e) { e.printStackTrace(); return e; } } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); mRequest.mCallback.onProgressUpdate(values[0], values[1]); } @Override protected void onPostExecute(Object result) { super.onPostExecute(result); if (result instanceof Exception) { mRequest.mCallback.onFailure((Exception) result); } else { mRequest.mCallback.onSuccess(result); } }}
HttpClientUtil
public class HttpClientUtil { public static HttpResponse execute(Request request) throws IOException { switch (request.method) { case GET: return get(request); case POST: return post(request); default: throw new IllegalStateException("the method" + request.method.name() + "doesn't support"); } } private static HttpResponse post(Request request) throws IOException { HttpClient client = new DefaultHttpClient(); HttpPost post = new HttpPost(request.url); addHeader(post, request.headers); if (request.entity == null) { throw new IllegalStateException("you forget to set post content to the HttpPost"); } else { post.setEntity(request.entity); } HttpResponse response = client.execute(post); return response; } private static HttpResponse get(Request request) throws IOException { HttpClient client = new DefaultHttpClient(); HttpGet get = new HttpGet(request.url); addHeader(get, request.headers); HttpResponse response = client.execute(get); return response; } public static void addHeader(HttpUriRequest request, Map<String, String> headers) { if (headers != null && headers.size() > 0) { for (Map.Entry<String, String> entry : headers.entrySet()) { request.addHeader(entry.getKey(), entry.getValue()); } } }}
测试
略过
说明
这里实现了常见的回调:进度、成功、失败,并且支持对多种格式的请求扩展。封装了httpurlconnection、httpclient两种方式。
很多问题:比如用的是异步任务等等,android3.0后,异步任务变为串行的了,并且异步任务的可扩展性非常的低,下面我们会讲解另一种实现方式:ThreadPool+Runnable+Handler,SimpleNet就是这种实现方式,后面也会专门写一篇博客来分析这个。
仅供学习使用
几个问题
Q:框架和库(工具包)的区别?
A:框架与库和工具包看起来很类似-都是提供了一系列api。他们的不同在于:库和工具包是为程序员带来自由的,框架则是为了给程序员带来约束的。具体来说:库和工具包是为了给程序员提供武器装备的,框架则利用控制反转机制实现对各模块的统一调度。Q:现在网络框架那么多,volley、universal image loader、fresco、Picasso、glide、okhttp,怎么办?
A:感觉重点是原理,都是为了解决同样的问题,比较来说,可能就是某个框架在解决某个问题时可能更出色一点而已.以上网络框架仅供学习使用,不要用在工业级项目中!!!
代码下载地址:
https://github.com/zhujainxipan/FYForAndroidTest
- 自己动手写HTTP框架:异步任务篇
- 自己动手写HTTP框架:ThreadPool+Runnable+handler篇
- 自己动手写 HTTP Server
- 自己动手写 HTTP Server
- 自己动手写 HTTP Server
- 自己动手写框架
- 自己动手写AJAX框架
- 自己动手写ajax框架
- 自己动手写MVC框架
- 自己动手写Struts框架
- 自己动手写注解框架
- [原创]自己动手写 HTTP Server
- 自己动手写HTTP服务--myhttpd
- 自己动手写web框架----1
- 自己动手写web框架----2
- 《自己动手写框架1》:缘起
- 自己动手写Rpc框架系列
- 自己动手写Android数据库框架
- 如何使用MySQL的 group_concat函数
- Activity的生命周期
- 工具类
- 【随记】cxf的webservice接口实现
- [Java并发包学习七]解密ThreadLocal
- 自己动手写HTTP框架:异步任务篇
- 分布式设计与开发(二)------几种必须了解的分布式算法
- DOM解析XML文件并添加到数据库中
- UIApplication深入研究
- opencv批处理提取图像的特征
- volley源码分析
- Java的递归
- 第十三周项目四立体类族共有的抽象类
- cocos 获得2个点形成的线段的角度