自己动手写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

0 0