封装android http框架

来源:互联网 发布:新闻联播图片素材软件 编辑:程序博客网 时间:2024/06/05 06:01

目前有几个开源的android http框架,比如volley、android-async-http,对于初学者来说听上去可能很高大上,实际就是对常用的网络请求代码做了一下封装,看过一套框架源码以后就会感觉没那么复杂,我们完全可以自己封装一个http框架。


需求分析:
1. 支持http协议:GET、POST、PUT、DELETE
2. 支持apache的HttpClient和原生的HttpURLConnection两种请求方式
3. 异步请求(使用AsyncTask或Thread+Handler)
4. 支持多线程上传下载(使用RandomAccessFile)
5. 请求错误统一处理(可自定义Exception)
6. 预处理服务端返回的数据
7. 上传下载进度更新
8. 支持断点续传
9. 随时取消网络请求
10. 关联activity(activity被回收时,请求应终止)


类图:
这里写图片描述


时序图:
这里写图片描述


关键代码:

public class RequestTask extends AsyncTask<Object, Integer, Object>{    private Request mRequest;    public RequestTask(Request request){        mRequest = request;    }    @Override    protected Object doInBackground(Object... params) {        try {            /* HttpClient请求方式            HttpResponse httpResponse = HttpClientUtils.request(mRequest);            return mRequest.mCallback.handleResponse(httpResponse, new ProgressCallback() {                @Override                public void onProgressUpdate(int curPos, int contentLength) {                    publishProgress(curPos, contentLength);                }            });            */            // HttpURLConnection请求方式            InputStream is = HttpUrlConnUtils.request(mRequest);            return mRequest.mCallback.handleResponse(is, new ProgressCallback() {                @Override                public void onProgressUpdate(int curPos, int contentLength) {                    publishProgress(curPos, contentLength);                }            });        } catch (Exception e) {            return e;        }    }    @Override    protected void onPostExecute(Object o) {        if(o instanceof Exception){            mRequest.mCallback.onFail((Exception) o);        }else{            mRequest.mCallback.onSuccess(o);        }    }    @Override    protected void onProgressUpdate(Integer... values) {        super.onProgressUpdate(values);        if(mRequest.mProgressCallback != null){            mRequest.mProgressCallback.onProgressUpdate(values[0], values[1]);        }    }}
public class HttpClientUtils {    public static HttpResponse request(Request request) throws Exception {        switch (request.mRequestMethod){            case GET:                return get(request);            case POST:                return post(request);            default:                throw new IllegalStateException("The request's request method is illegal");        }    }    public static HttpResponse get(Request request) throws Exception{        HttpClient httpClient = new DefaultHttpClient();        HttpGet httpGet = new HttpGet(request.mUrl);        addHeader(httpGet, request.mHeaderMap);        return httpClient.execute(httpGet);    }    public static HttpResponse post(Request request) throws Exception{        HttpClient httpClient = new DefaultHttpClient();        HttpPost httpPost = new HttpPost(request.mUrl);        httpPost.setEntity(new StringEntity(request.mPostContent));        addHeader(httpPost, request.mHeaderMap);        return httpClient.execute(httpPost);    }    public static void addHeader(HttpUriRequest httpUriRequest, Map<String, String> headers){        if(headers == null){            return;        }        for (Map.Entry<String, String> entry : headers.entrySet()) {            httpUriRequest.addHeader(entry.getKey(), entry.getValue());        }    }}
public class HttpUrlConnUtils {    public static InputStream request(Request request){        switch (request.mRequestMethod){            case GET:                return get(request);            case POST:                return post(request);        }        return null;    }    private static InputStream get(Request request) {        try {            URL url = new URL(request.mUrl);            HttpURLConnection conn = (HttpURLConnection) url.openConnection();            conn.setRequestMethod("GET");            conn.setConnectTimeout(5000);            addHeader(conn, request.mHeaderMap);            return conn.getInputStream();        } catch (MalformedURLException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        return null;    }    private static InputStream post(Request request) {        try {            URL url = new URL(request.mUrl);            HttpURLConnection conn = (HttpURLConnection) url.openConnection();            conn.setRequestMethod("POST");            conn.setConnectTimeout(5000);            addHeader(conn, request.mHeaderMap);            conn.setDoInput(true);            if(!TextUtils.isEmpty(request.mPostContent)){                conn.setDoOutput(true);                OutputStreamWriter osw = new OutputStreamWriter(conn.getOutputStream(), "utf-8");                BufferedWriter bw = new BufferedWriter(osw);                bw.write(request.mPostContent);                bw.flush();            }            return conn.getInputStream();        } catch (MalformedURLException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        return null;    }    private static void addHeader(HttpURLConnection conn, Map<String, String> headerMap) {        if(headerMap == null){            return;        }        conn.addRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727)");        for(Map.Entry<String, String> entry : headerMap.entrySet()){            conn.addRequestProperty(entry.getKey(), entry.getValue());        }    }}
public abstract class AbstractCallback implements ICallback{    public String path;    //处理HttpURLConnection请求方式的返回流    @Override    public Object handleResponse(InputStream inputStream, ProgressCallback callback) {        try {            if(!TextUtils.isEmpty(path)){                FileOutputStream fos = new FileOutputStream(path);                byte[] b = new byte[1024];                int len;                while((len = inputStream.read(b)) != -1){                    fos.write(b, 0, len);                }                fos.flush();                inputStream.close();                fos.close();                return IOUtils.readFromFile(path);            }else{                return IOUtils.inputStream2Str(inputStream);            }        } catch (FileNotFoundException e) {            e.printStackTrace();            return e;        } catch (IOException e) {            e.printStackTrace();            return e;        }    }    //处理HttpClient请求方式的返回实体    public Object handleResponse(HttpResponse httpResponse, ProgressCallback progressCallback){        try {            HttpEntity httpEntity = httpResponse.getEntity();            int statusCode = httpResponse.getStatusLine().getStatusCode();            switch (statusCode){                case HttpStatus.SC_OK:                    if(!TextUtils.isEmpty(path)){                        FileOutputStream fos = new FileOutputStream(path);                        InputStream is = httpEntity.getContent();                        byte[] b = new byte[1024];                        int len;                        int curPos = 0;                        int contentLength = (int) httpEntity.getContentLength();                        while((len = is.read(b)) != -1){                            curPos += len;                            fos.write(b, 0, len);                            if(progressCallback != null){                                progressCallback.onProgressUpdate(curPos, contentLength);                            }                        }                        fos.flush();                        is.close();                        fos.close();                        return bindData(path);                    }else{                        return bindData(EntityUtils.toString(httpEntity));                    }            }        } catch (IOException e) {            e.printStackTrace();            return e;        }        return null;    }    //子类需复写该方法    protected Object bindData(String content) {        return content;    }    public AbstractCallback setPath(String path){        this.path = path;        return this;    }}
public abstract class StringCallback extends AbstractCallback{    @Override    protected Object bindData(String content) {        if(!TextUtils.isEmpty(path)){            return IOUtils.readFromFile(path);        }else{            return content;        }    }}
public class IOUtils {    public static String readFromFile(String path){        ByteArrayOutputStream outputStream = null;        FileInputStream fis = null;        try {            outputStream = new ByteArrayOutputStream(4 * 1024);            File file = new File(path);            fis = new FileInputStream(file);            byte[] b = new byte[1024];            int len;            while((len = fis.read(b)) != -1){                outputStream.write(b, 0, len);            }            outputStream.flush();            return new String(outputStream.toByteArray());        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } finally {            if(outputStream != null){                try {                    outputStream.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if(fis != null){                try {                    fis.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }        return null;    }    public static String inputStream2Str(InputStream is){        try {            String str;            StringBuffer sb = new StringBuffer();            BufferedReader br = new BufferedReader(new InputStreamReader(is, "utf-8"));            while((str = br.readLine()) != null){                sb.append(str);            }            return sb.toString();        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        return "";    }}

说明:

1.如需要实时显示当前上传下载进度的百分比,就需要有服务端返回实体的总长度。但拿到HttpResponse的HttpEntity时,调用getContentLength()结果却是-1。解决方案:
request之前添加header,伪装成浏览器:

httpGet.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727)");

2.关于多线程分段下载和断点续传的实现,后续博客会有更新。

0 0