自己动手写HTTP框架:异步任务篇

需求

  • 2011年的某一天,你的公司初进移动开发领域,上司要求你实现一套http网络请求框架,要求易扩展。

  • 2011年!!!volley、imageloader、fresco、glide…..都是啥?????

需求分析

  • 支持http协议get、post、put、delete
  • 异步请求
  • 请求错误统一处理
  • 支持多文件上传
  • 预处理服务器返回的数据
  • 上传/下载 进度更新
  • 取消请求
  • 可扩展需求:
  • timeout重试机制
  • 多任务队列
  • 关联activity
  • 缓存刷新机制

系统分析与设计

自己动手写HTTP框架:异步任务篇_第1张图片

核心模块分析(编码)

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框架:异步任务篇)