xUtils之HttpUtils

HttpUtils模块的底层是使用的HttpClient

HttpUtils模块支持Restful风格的四种请求方式(GETPOSTPUTDELETE)。

下面是在HttpUtils模块基础上封装的接口:

package com.fpliu.newton.framework.net;

import android.text.TextUtils;

import com.fpliu.newton.framework.util.DebugLog;

import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.http.HttpHandler;
import com.lidroid.xutils.http.RequestParams;
import com.lidroid.xutils.http.callback.RequestCallBack;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

/**
 * 网络请求 - 使用XUtils开源库
 * https://github.com/leleliu008/xutils
 * 支持Restful API的GET、POST、PUT、DELETE四种方式
 *
 * @author 792793182@qq.com 2015-06-15
 */
public final class XUtilsRequest {

    private static final String TAG = XUtilsRequest.class.getSimpleName();

    /**
     * 请求头
     */
    private static final String HEADER_ACCEPT = "Accept";
    private static final String HEADER_CONNECTION = "Connection";
    private static final String HEADER_AUTHORIZATION = "Authorization";
    private static final String HEADER_USER_AGENT = "User-Agent";
    private static final String HEADER_REFERER = "Referer";
    private static final String HEADER_CONTENT_TYPE = "Content-Type";

    /**
     * 请求体格式 - 表单
     */
    private static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";

    /**
     * 请求体格式 - JSON
     */
    private static final String CONTENT_TYPE_JSON = "application/json";

    /**
     * 请求体格式 - XML
     */
    private static final String CONTENT_TYPE_XML = "application/xml";

    private static final CookieStore cookieStore = new BasicCookieStore();
    
    private XUtilsRequest() {

    }

    /**
     * 下载
     *
     * @param url             资源路径
     * @param desFilePath     本地路径
     * @param requestCallBack 请求回调
     */
    public static void asyncDownload(String url, String desFilePath, RequestCallBack<File> requestCallBack) {
        RequestParams requestParams = new RequestParams();
        HttpUtils http = newHttpUtils(requestParams, url, "");
        HttpHandler handler = http.download(url,
                desFilePath,
                requestParams,
                true, // 如果目标文件存在,接着未完成的部分继续下载。服务器不支持RANGE时将从新下载。
                false,
                requestCallBack);
    }

    /**
     * GET请求
     *
     * @param url           请求资源的路径
     * @param authorization 验证用户,不需要验证的,传入空,即可
     * @param params        URL的参数
     * @param callback      请求的回调
     */
    public static <K> void asyncGet(String url, String authorization, List<BasicNameValuePair> params, RequestCallBack<K> callback) {
        RequestParams requestParams = new RequestParams();

        url = getUrl(url, params);
        HttpUtils httpUtils = newHttpUtils(requestParams, url, authorization);
        httpUtils.send(com.lidroid.xutils.http.client.HttpRequest.HttpMethod.GET, url, requestParams, callback);
    }

    /**
     * POST请求,请求体是表单,是异步请求
     *
     * @param url           请求资源的路径
     * @param authorization 验证用户,不需要验证的,传入空,即可
     * @param params        请求参数
     * @param callback      请求的回调
     */
    public static <K> void asyncPostForm(String url, String authorization, List<BasicNameValuePair> params, RequestCallBack<K> callback) {
        RequestParams requestParams = new RequestParams();
        requestParams.setHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_FORM);
        requestParams.setBodyEntity(getFormEntity(params));

        HttpUtils httpUtils = newHttpUtils(requestParams, url, authorization);
        httpUtils.send(com.lidroid.xutils.http.client.HttpRequest.HttpMethod.POST, url, requestParams, callback);
    }

    /**
     * POST请求,请求体是表单,是异步请求
     *
     * @param url           请求资源的路径
     * @param authorization 验证用户,不需要验证的,传入空,即可
     * @param params        请求参数
     * @param callback      请求的回调
     */
    public static <K> void asyncPostForm(String url, String authorization, RequestCallBack<K> callback, String... params) {
        RequestParams requestParams = new RequestParams();
        requestParams.setHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_FORM);
        requestParams.setBodyEntity(getFormEntity(params));

        HttpUtils httpUtils = newHttpUtils(requestParams, url, authorization);
        httpUtils.send(com.lidroid.xutils.http.client.HttpRequest.HttpMethod.POST, url, requestParams, callback);
    }

    /**
     * POST请求,请求体是JSON,是异步请求
     *
     * @param url           请求资源的路径
     * @param authorization 验证用户,不需要验证的,传入空,即可
     * @param json          JSON字符串,使用String类型可以支持GSON、fastJson、json-lib等库的转化,而不局限于一种
     * @param callback      请求的回调
     */
    public static <K> void asyncPostJson(String url, String authorization, String json, RequestCallBack<K> callback) {
        RequestParams requestParams = new RequestParams();
        requestParams.setHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_JSON);
        requestParams.setBodyEntity(getStringEntity(json, CONTENT_TYPE_JSON));

        HttpUtils httpUtils = newHttpUtils(requestParams, url, authorization);
        httpUtils.send(com.lidroid.xutils.http.client.HttpRequest.HttpMethod.POST, url, requestParams, callback);
    }

    /**
     * POST请求,请求体是XML,是异步请求
     *
     * @param url           请求资源的路径
     * @param authorization 验证用户,不需要验证的,传入空,即可
     * @param xml           XML字符串
     * @param callback      请求的回调
     */
    public static <K> void asyncPostXml(String url, String authorization, String xml, RequestCallBack<K> callback) {
        RequestParams requestParams = new RequestParams();
        requestParams.setHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_XML);
        requestParams.setBodyEntity(getStringEntity(xml, CONTENT_TYPE_XML));

        HttpUtils httpUtils = newHttpUtils(requestParams, url, authorization);
        httpUtils.send(com.lidroid.xutils.http.client.HttpRequest.HttpMethod.POST, url, requestParams, callback);
    }

    /**
     * PUT请求,请求体是表单,是异步请求
     *
     * @param url           请求资源的路径
     * @param authorization 验证用户,不需要验证的,传入空,即可
     * @param params        请求参数对,语法糖
     * @param callback      请求的回调
     */
    public static <K> void asyncPutForm(String url, String authorization, List<BasicNameValuePair> params, RequestCallBack<K> callback) {
        RequestParams requestParams = new RequestParams();
        requestParams.setHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_FORM);
        requestParams.setBodyEntity(getFormEntity(params));

        HttpUtils httpUtils = newHttpUtils(requestParams, url, authorization);
        httpUtils.send(com.lidroid.xutils.http.client.HttpRequest.HttpMethod.PUT, url, requestParams, callback);
    }

    /**
     * PUT请求,请求体是表单,是异步请求
     *
     * @param url           请求资源的路径
     * @param authorization 验证用户,不需要验证的,传入空,即可
     * @param params        请求参数
     * @param callback      请求的回调
     */
    public static <K> void asyncPutForm(String url, String authorization, RequestCallBack<K> callback, String... params) {
        RequestParams requestParams = new RequestParams();
        requestParams.setHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_FORM);
        requestParams.setBodyEntity(getFormEntity(params));

        HttpUtils httpUtils = newHttpUtils(requestParams, url, authorization);
        httpUtils.send(com.lidroid.xutils.http.client.HttpRequest.HttpMethod.PUT, url, requestParams, callback);
    }

    /**
     * PUT请求,请求体是JSON,是异步请求
     *
     * @param url           请求资源的路径
     * @param authorization 验证用户,不需要验证的,传入空,即可
     * @param json          JSON字符串,使用String类型可以支持GSON、fastJson、json-lib等库的转化,而不局限于一种
     * @param callback      请求的回调
     */
    public static <K> void asyncPutJson(String url, String authorization, String json, RequestCallBack<K> callback) {
        RequestParams requestParams = new RequestParams();
        requestParams.setHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_JSON);
        requestParams.setBodyEntity(getStringEntity(json, CONTENT_TYPE_JSON));

        HttpUtils httpUtils = newHttpUtils(requestParams, url, authorization);
        httpUtils.send(com.lidroid.xutils.http.client.HttpRequest.HttpMethod.PUT, url, requestParams, callback);
    }

    /**
     * PUT请求,请求体是XML,是异步请求
     *
     * @param url           请求资源的路径
     * @param authorization 验证用户,不需要验证的,传入空,即可
     * @param xml           XML字符串
     * @param callback      请求的回调
     */
    public static <K> void asyncPutXml(String url, String authorization, String xml, RequestCallBack<K> callback) {
        RequestParams requestParams = new RequestParams();
        requestParams.setHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_XML);
        requestParams.setBodyEntity(getStringEntity(xml, CONTENT_TYPE_XML));

        HttpUtils httpUtils = newHttpUtils(requestParams, url, authorization);
        httpUtils.send(com.lidroid.xutils.http.client.HttpRequest.HttpMethod.PUT, url, requestParams, callback);
    }

    /**
     * DELETE请求
     *
     * @param url           请求资源的路径
     * @param authorization 验证用户,不需要验证的,传入空,即可
     * @param params        URL的参数
     * @param callback      请求的回调
     */
    public static <K> void asyncDelete(String url, String authorization, List<BasicNameValuePair> params, RequestCallBack<K> callback) {
        RequestParams requestParams = new RequestParams();

        url = getUrl(url, params);
        HttpUtils httpUtils = newHttpUtils(requestParams, url, authorization);

        httpUtils.send(com.lidroid.xutils.http.client.HttpRequest.HttpMethod.DELETE, url, requestParams, callback);
    }

    /**
     * 组装URL
     *
     * @param url
     * @param params
     * @return
     */
    private static String getUrl(String url, List<BasicNameValuePair> params) {
        String urlParams = "";
        if (params != null && !params.isEmpty()) {
            urlParams = URLEncodedUtils.format(params, "UTF-8");
        }

        if (!TextUtils.isEmpty(urlParams)) {
            url += "?" + urlParams;
        }

        return url;
    }

    public static UrlEncodedFormEntity getFormEntity(String... kvs) {
        if (kvs != null) {
            int length = kvs.length / 2;
            if (length > 0) {
                ArrayList<BasicNameValuePair> nvs = new ArrayList<BasicNameValuePair>(length);
                for (int i = 0; i < length; i += 2) {
                    //如果Key为空,就跳过
                    if (TextUtils.isEmpty(kvs[i])) {
                        continue;
                    }
                    nvs.add(new BasicNameValuePair(kvs[i], kvs[i + 1]));
                }

                if (!nvs.isEmpty()) {
                    return getFormEntity(nvs);
                }
            }
        }
        return null;
    }

    /**
     * 获取表单实体
     *
     * @param params
     * @return
     */
    public static UrlEncodedFormEntity getFormEntity(List<BasicNameValuePair> params) {
        UrlEncodedFormEntity entity = null;
        try {
            entity = new UrlEncodedFormEntity(params, HTTP.UTF_8);
        } catch (UnsupportedEncodingException e) {
            DebugLog.e(TAG, "getFormEntity()", e);
        }

        return entity;
    }

    /**
     * 获取字符串类型的请求体
     *
     * @param str
     * @param contentType
     * @return
     */
    private static StringEntity getStringEntity(String str, String contentType) {
        StringEntity stringEntity = null;
        try {
            stringEntity = new StringEntity(str, HTTP.UTF_8);
            stringEntity.setContentType(contentType);
        } catch (UnsupportedEncodingException e) {
            DebugLog.e(TAG, "getStringEntity()", e);
        }
        return stringEntity;
    }

    /**
     * POST请求
     *
     * @param url
     * @param contentHeader
     * @param entity
     * @param callback
     * @return
     */
    private static <K> void post(String url, String contentHeader, String authorization, HttpEntity entity, RequestCallBack<K> callback) {
        RequestParams requestParams = new RequestParams();
        requestParams.setHeader(HEADER_CONTENT_TYPE, contentHeader);
        requestParams.setBodyEntity(entity);

        HttpUtils httpUtils = newHttpUtils(requestParams, url, authorization);
        httpUtils.send(com.lidroid.xutils.http.client.HttpRequest.HttpMethod.POST, url, requestParams, callback);
    }

    public static HttpUtils newHttpUtils(RequestParams requestParams, final String url, String authorization) {
        if (!TextUtils.isEmpty(authorization)) {
            requestParams.setHeader(HEADER_AUTHORIZATION, authorization);
        }

        HttpUtils httpUtils = new HttpUtils();
        httpUtils.configUserAgent("Android");
        // 设置重试次数
        httpUtils.configRequestRetryCount(3);
        httpUtils.configResponseTextCharset("UTF-8");

        // 设超时时间
        httpUtils.configTimeout(3 * 1000);
        // 设置缓存存活时间为10秒
        httpUtils.configCurrentHttpCacheExpiry(10 * 1000);

        //配置Cookie的保存
        httpUtils.configCookieStore(cookieStore);

        DefaultHttpClient httpClient = (DefaultHttpClient) httpUtils.getHttpClient();
        // 拦截请求,打印日志
        httpClient.addRequestInterceptor(new HttpRequestInterceptor() {

            @Override
            public void process(HttpRequest httpRequest, HttpContext httpContext)
                    throws org.apache.http.HttpException, IOException {
                DebugLog.d(TAG, HTTPUtil.getRequestInfo(httpRequest));
            }
        });
        // 拦截响应,打印日志
        httpClient.addResponseInterceptor(new HttpResponseInterceptor() {

            @Override
            public void process(HttpResponse response, HttpContext httpContext)
                    throws org.apache.http.HttpException, IOException {
                // 看看有没有Content-Type头
                Header[] headers = response.getHeaders("Content-Type");
                if (headers == null || headers.length == 0) {
                    DebugLog.d(TAG, HTTPUtil.getResponseInfo(response, url, ""));
                    return;
                }

                String contentType = headers[0].getValue();
                if (contentType.contains("application/x-www-form-urlencoded")
                        || contentType.contains("application/json")
                        || contentType.contains("application/xml")
                        || contentType.contains("text/html")) {

                    // 看看有没有返回实体
                    HttpEntity entity = response.getEntity();
                    if (entity == null) {
                        DebugLog.d(TAG, HTTPUtil.getResponseInfo(response, url, ""));
                        return;
                    }

                    String entityStr = "";
                    try {
                        entityStr = EntityUtils.toString(entity);
                    } catch (Exception e) {
                        DebugLog.e(TAG, "", e);
                    }
                    DebugLog.d(TAG, HTTPUtil.getResponseInfo(response, url, entityStr));

                    // 如果这个实体是不可重用的,就重新设置一下
                    if (!entity.isRepeatable()) {
                        response.setEntity(new StringEntity(entityStr, "utf-8"));
                    }
                } else {
                    DebugLog.d(TAG, HTTPUtil.getResponseInfo(response, url, ""));
                }
            }
        });

        return httpUtils;
    }
}

HttpUtils模块本身有一个缺点,就是他没有打印任何的日志, 我们想要观察Http请求和响应过程,就必须自己打印日志。 原理是:注册Http请求的拦截器和Http响应的拦截器。打印相关的日志。

下面是打印日志的实现代码:

HttpUtils httpUtils = new HttpUtils();
DefaultHttpClient httpClient = (DefaultHttpClient) httpUtils.getHttpClient();
// 拦截请求,打印日志
httpClient.addRequestInterceptor(new HttpRequestInterceptor() {

    @Override
    public void process(HttpRequest httpRequest, HttpContext httpContext)
            throws org.apache.http.HttpException, IOException {
        DebugLog.d(TAG, HTTPUtil.getRequestInfo(httpRequest));
    }
});
// 拦截响应,打印日志
httpClient.addResponseInterceptor(new HttpResponseInterceptor() {

    @Override
    public void process(HttpResponse response, HttpContext httpContext)
            throws org.apache.http.HttpException, IOException {
        // 看看有没有Content-Type头
        Header[] headers = response.getHeaders("Content-Type");
        if (headers == null || headers.length == 0) {
            DebugLog.d(TAG, HTTPUtil.getResponseInfo(response, url, ""));
            return;
        }

        String contentType = headers[0].getValue();
        if (contentType.contains("application/x-www-form-urlencoded")
                || contentType.contains("application/json")
                || contentType.contains("application/xml")
                || contentType.contains("text/html")) {

            // 看看有没有返回实体
            HttpEntity entity = response.getEntity();
            if (entity == null) {
                DebugLog.d(TAG, HTTPUtil.getResponseInfo(response, url, ""));
                return;
            }

            String entityStr = "";
            try {
                entityStr = EntityUtils.toString(entity);
            } catch (Exception e) {
                DebugLog.e(TAG, "", e);
            }
            DebugLog.d(TAG, HTTPUtil.getResponseInfo(response, url, entityStr));

            // 如果这个实体是不可重用的,就重新设置一下
            if (!entity.isRepeatable()) {
                response.setEntity(new StringEntity(entityStr, "utf-8"));
            }
        } else {
            DebugLog.d(TAG, HTTPUtil.getResponseInfo(response, url, ""));
        }
    }
});

HTTPUtil.java的内容如下:

package com.fpliu.newton.framework.net;

import android.text.TextUtils;

import com.lidroid.xutils.http.client.multipart.MultipartEntity;
import com.fpliu.newton.framework.util.DebugLog;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.util.EntityUtils;

public final class HTTPUtil {

    private static final String TAG = HTTPUtil.class.getSimpleName();

    private HTTPUtil() {

    }

    /**
     * 获取请求信息
     *
     * @param httpRequest HTTP请求
     * @return HTTP协议内容
     */
    public static String getRequestInfo(HttpRequest httpRequest) {
        StringBuilder builder = new StringBuilder("\n----------------------- request start ------------------------\n");

        //请求行
        builder.append(httpRequest.getRequestLine()).append('\n');

        //请求头
        for (Header header : httpRequest.getAllHeaders()) {
            builder.append(header.getName()).append(':').append(header.getValue()).append('\n');
        }

        if (httpRequest instanceof HttpPost) {
            //空行
            builder.append('\n');

            //请求体
            HttpEntity httpEntity = ((HttpPost) httpRequest).getEntity();
            if (httpEntity instanceof MultipartEntity) {
                MultipartEntity multipartEntity = (MultipartEntity) httpEntity;
                builder.append(multipartEntity);
            } else {
                try {
                    builder.append(EntityUtils.toString(httpEntity));
                } catch (Exception e) {
                    DebugLog.e(TAG, "getRequestInfo()", e);
                }
            }

            builder.append("\n----------------------- request end ------------------------\n");
        }

        return builder.toString();
    }

    /**
     * 获取响应信息
     *
     * @param response 响应
     * @return HTTP协议内容
     */
    public static String getResponseInfo(HttpResponse response, String url, String entity) {
        StringBuilder builder = new StringBuilder("\n----------------------- response start ------------------------\n");

        builder.append("url:" + url + "\n");
        //状态行
        builder.append(response.getStatusLine()).append('\n');

        //响应头
        for (Header header : response.getAllHeaders()) {
            builder.append(header.getName()).append(':').append(header.getValue()).append('\n');
        }

        //空行
        builder.append('\n');

        //响应体
        builder.append(entity);

        builder.append("\n----------------------- response end ------------------------\n");

        return builder.toString();
    }
}

HttpUtils模块本身还有一个缺点,就是他没有提供对数据解析的接口, 这一点做的最好的应该是AQuery