Android HttpClient HttpUrlConnection用法总结

HttpClient

这个不用说了,Apache的API,但是不推荐使用了,在最新的api中甚至都把HttpClient去掉了。但是还是有必要掌握下HttpClient的用法的

GET方式

//先将参数放入List,再对参数进行URL编码
List params = new LinkedList();
params.add(new BasicNameValuePair("param1", "中国"));
params.add(new BasicNameValuePair("param2", "value2"));

//对参数编码
String param = URLEncodedUtils.format(params, "UTF-8");

//baseUrl           
String baseUrl = "http://ubs.free4lab.com/php/method.php";

//将URL与参数拼接
HttpGet getMethod = new HttpGet(baseUrl + "?" + param);

HttpClient httpClient = new DefaultHttpClient();

try {
    HttpResponse response = httpClient.execute(getMethod); //发起GET请求

    Log.i(TAG, "resCode = " + response.getStatusLine().getStatusCode()); //获取响应码
    Log.i(TAG, "result = " + EntityUtils.toString(response.getEntity(), "utf-8"));//获取服务器响应内容
} catch (ClientProtocolException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

POST方法

//和GET方式一样,先将参数放入List
params = new LinkedList();
params.add(new BasicNameValuePair("param1", "Post方法"));
params.add(new BasicNameValuePair("param2", "第二个参数"));

try {
    HttpPost postMethod = new HttpPost(baseUrl);
    postMethod.setEntity(new UrlEncodedFormEntity(params, "utf-8")); //将参数填入POST Entity中

    HttpResponse response = httpClient.execute(postMethod); //执行POST方法
    Log.i(TAG, "resCode = " + response.getStatusLine().getStatusCode()); //获取响应码
    Log.i(TAG, "result = " + EntityUtils.toString(response.getEntity(), "utf-8")); //获取响应内容

} catch (UnsupportedEncodingException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (ClientProtocolException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

HttpClient传递复杂的参数,比如文件 需要额外的jar包

常见问题

异常处理
协议异常ClientProtocolException: 无效的cookie 很多http请求需要提供登录凭证 如果没有则协议异常
连接超时SocketTimeoutException: 无法连接到服务器比如服务器不可用 那么就连接超时
套接字超时ConnectTimeoutException: 既定时间内未收到服务器响应此时可以连接到服务器 但是未收到响应 那么套接字超时

HttpConnectionparam.setconnectiontimeout(param,5000)  //连接超时时间
HttpConnectionparam.setsotimeout(param,5000) //套接字连接超时
ConnManagerParams.settimeout(param,5000)  //连接管理器的超时 定义应用程序等待多久才让一个连接退出连接池的管理  比如连接池已经满了 新的连接就必须要等待了

使用try catch块来捕获

多线程问题
可以设置单例的httpclient 这样就可以共享设置的连接参数,但是单例的话 多线程就会有问题
HttpClient httpClient = new DefaultHttpClient(); 有多线程问题
比如两个线程都调用httpClient.execute(getMethod); getMethod使用的是同一个HttpGet对象 那么毫无疑问多线程问题

HttpConnectionparam.setconnectiontimeout(params,5000)
HttpConnectionparam.setsotimeout(params,5000)
ConnManagerParams.settimeout(params,5000)
HttpClient httpClient = new DefaultHttpClient(ThreadSafeClientManager conMgr,HttpParams params);

ThreadSafeClientManager负责管理所有的httpclient的http连接 内部实现不会有多线程问题
此时的HttpParams作用在HttpClient上,所以可以供所有的HttpGet HttpPost对像共享

如果要定义自己的连接参数 那么可以

HttpGet getMethod = new HttpGet(baseUrl + "?" + param);
HttpParams params= getMethod.getarams();
HttpConnectionparam.setconnectiontimeout(param,5000);
getMethod.setparams(params);

此时的HttpParams作用在HttpGet上,不会影响到HttpClient共享的连接参数

AndroidHttpClient

HttpClient子类 适用于android开发人员 连接超时/套接字超时默认20s 默认是线程安全的ThreadSafeClientManager

HttpUrlConnection

HttpUrlConnection属于Java的标准类库
更轻量了 推荐使用这个 4.4开始底层通过Okhttp来实现

HttpURLConnection的多线程问题? 因为采取Okhttp的底层实现,是木有多线程的问题的,具体是为什么,需要研究

HttpURLConnection使用

1. HttpURLConnection连接URL
1)创建一个URL对象
URL url = new URL(http://www.baidu.com);

2)利用HttpURLConnection对象从网络中获取网页数据
HttpURLConnection conn = (HttpURLConnection) url.openConnection();

3)设置连接超时
conn.setConnectTimeout(6*1000);

4)对响应码进行判断
if (conn.getResponseCode() != 200) //从Internet获取网页,发送请求,将网页以流的形式读回来
throw new RuntimeException(“请求url失败”);

5)得到网络返回的输入流
InputStream is = conn.getInputStream();
6)String result = readData(is, “GBK”); //文件流输入出文件用outStream.write
7)conn.disconnect();

总结:
- 记得设置连接超时,如果网络不好,Android系统在超过默认时间会收回资源中断操作.
- 返回的响应码200,是成功.
- 在Android中对文件流的操作和JAVA SE上面是一样的.
- 在对大文件的操作时,要将文件写到SDCard上面,不要直接写到手机内存上.
- 操作大文件是,要一遍从网络上读,一遍要往SDCard上面写,减少手机内存的使用.
- 对文件流操作完,要记得及时关闭.

2. 向Internet发送请求参数
步骤:
1)创建URL对象:URL realUrl = new URL(requestUrl);
2)通过HttpURLConnection对象,向网络地址发送请求
HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
3)设置容许输出:conn.setDoOutput(true);
4)设置不使用缓存:conn.setUseCaches(false);
5)设置使用POST的方式发送:conn.setRequestMethod(“POST”);
6)设置维持长连接:conn.setRequestProperty(“Connection”, “Keep-Alive”);
7)设置文件字符集:conn.setRequestProperty(“Charset”, “UTF-8”);
8)设置文件长度:conn.setRequestProperty(“Content-Length”, String.valueOf(data.length));
9)设置文件类型:conn.setRequestProperty(“Content-Type”,”application/x-www-form-urlencoded”);
10)以流的方式输出.
总结:
- 发送POST请求必须设置允许输出
- 不要使用缓存,容易出现问题.
- 在开始用HttpURLConnection对象的setRequestProperty()设置,就是生成HTML文件头.

3. 向Internet发送xml数据
XML格式是通信的标准语言,Android系统也可以通过发送XML文件传输数据.
1)将生成的XML文件写入到byte数组中,并设置为UTF-8:byte[] xmlbyte = xml.toString().getBytes(“UTF-8”);
2)创建URL对象,并指定地址和参数:URL url = new URL(http://localhost:8080/itcast/contanctmanage.do?method=readxml);
3)获得链接:HttpURLConnection conn = (HttpURLConnection) url.openConnection();
4)设置连接超时:conn.setConnectTimeout(6* 1000);
5)设置允许输出conn.setDoOutput(true);
6)设置不使用缓存:conn.setUseCaches(false);
7)设置以POST方式传输:conn.setRequestMethod(“POST”);
8)维持长连接:conn.setRequestProperty(“Connection”, “Keep-Alive”);
9)设置字符集:conn.setRequestProperty(“Charset”, “UTF-8”);
10)设置文件的总长度:conn.setRequestProperty(“Content-Length”, String.valueOf(xmlbyte.length));
11)设置文件类型:conn.setRequestProperty(“Content-Type”, “text/xml; charset=UTF-8”);
12)以文件流的方式发送xml数据:outStream.write(xmlbyte);
总结:
- 我们使用的是用HTML的方式传输文件,这个方式只能传输一般在5M一下的文件.
- 传输大文件不适合用HTML的方式,传输大文件我们要面向Socket编程.确保程序的稳定性
- 将地址和参数存到byte数组中:byte[] data = params.toString().getBytes();

HttpClient还是HttpUrlConnection

参考 Android访问网络,使用HttpURLConnection还是HttpClient?
在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。
而在Android 2.3版本及以后,HttpURLConnection则是最佳的选择。它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。对于新的应用程序应该更加偏向于使用HttpURLConnection,因为在以后的工作当中我们也会将更多的时间放在优化HttpURLConnection上面。

其实现在嘛,两者都不用,就用Okhttp
HttpUrlConnection现在的底层实现就是通过Okhttp

HttpURLConnection java的标准类(java.net),什么都没封装,用起来太原始,不方便
HttpClient Apache开源框架,提供对HTTP协议访问的封装,包括http的请求头,参数,内容体,响应等及多线程的应用。
HttpClient方便,但是封装了太多,也可能导致网络请求速度变慢,灵活度不够
HttpClient就是一个增强版的HttpURLConnection ,HttpURLConnection可以做的事情 HttpClient全部可以做;HttpURLConnection没有提供的有些功能,HttpClient也提供了

Volley中两者的使用

我们知道Volley中api<=9使用的是HttpClientStack,内部使用HttpClient。api>9使用的是HurlStack,内部使用HttpUrlConnection

先来看HttpClientStack,使用起来很简单

    private static void addHeaders(HttpUriRequest httpRequest, Map headers) {
        for (String key : headers.keySet()) {
            httpRequest.setHeader(key, headers.get(key));
        }
    }
    @Override
    public HttpResponse performRequest(Request request, Map additionalHeaders)
            throws IOException, AuthFailureError {
        HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
        addHeaders(httpRequest, additionalHeaders);  //缓存中的头
        addHeaders(httpRequest, request.getHeaders()); //request中的设置的头
        onPrepareRequest(httpRequest);
        HttpParams httpParams = httpRequest.getParams();
        int timeoutMs = request.getTimeoutMs();
        // TODO: Reevaluate this connection timeout based on more wide-scale
        // data collection and possibly different for wifi vs. 3G.
        HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
        HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
        return mClient.execute(httpRequest); //调用HttpClient的excute方法,执行网络请求,最后直接返回HttpResponse
    }
    static HttpUriRequest createHttpRequest(Request request,
            Map additionalHeaders) throws AuthFailureError {
            case Method.GET:
                return new HttpGet(request.getUrl());
            case Method.POST: {
                HttpPost postRequest = new HttpPost(request.getUrl());
                postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
                setEntityIfNonEmptyBody(postRequest, request); //设置post请求参数
                return postRequest;
            ........
            }
            default:
                throw new IllegalStateException("Unknown request method.");
        }
    private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest,
            Request request) throws AuthFailureError {
        byte[] body = request.getBody();
        if (body != null) {
            HttpEntity entity = new ByteArrayEntity(body);
            httpRequest.setEntity(entity); //setEntity方法设置post请求参数
        }
    }

看吧,使用起来很方便简单,都是直接封装好了,,,下面来看HurlStack,使用起来就更负责了

   @Override
    public HttpResponse performRequest(Request request, Map additionalHeaders)
            throws IOException, AuthFailureError {
        String url = request.getUrl();
        HashMap map = new HashMap();
        map.putAll(request.getHeaders()); //取出request设置的head
        map.putAll(additionalHeaders); //取出缓存中的Header,如果该request前面发起过网络请求,那么就会缓存下来,同时会把http的head也缓存
        ........
        URL parsedUrl = new URL(url);
        HttpURLConnection connection = openConnection(parsedUrl, request); //初始化HttpURLConnection对象
        for (String headerName : map.keySet()) {
            connection.addRequestProperty(headerName, map.get(headerName)); //设置Http请求的head
        }
        setConnectionParametersForRequest(connection, request);
        // Initialize HttpResponse with data from the HttpURLConnection.
        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
        int responseCode = connection.getResponseCode();
        if (responseCode == -1) {
            // -1 is returned by getResponseCode() if the response code could not be retrieved.
            // Signal to the caller that something was wrong with the connection.
            throw new IOException("Could not retrieve response code from HttpUrlConnection.");
        }
        StatusLine responseStatus = new BasicStatusLine(protocolVersion,
                connection.getResponseCode(), connection.getResponseMessage());
        BasicHttpResponse response = new BasicHttpResponse(responseStatus); //HttpResponse需要自己创建
        if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
            response.setEntity(entityFromConnection(connection)); //调用HttpResponse的setEntity方法设置相应内容
        }
        for (Entry> header : connection.getHeaderFields().entrySet()) {
            if (header.getKey() != null) {
                Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
                response.addHeader(h); //调用HttpResponse的addHeader方法添加响应头
            }
        }
        return response;
    }
    private HttpURLConnection openConnection(URL url, Request request) throws IOException {
        HttpURLConnection connection = createConnection(url);

        int timeoutMs = request.getTimeoutMs();
        connection.setConnectTimeout(timeoutMs); //设置连接超时时间
        connection.setReadTimeout(timeoutMs); 
        connection.setUseCaches(false); //不使用缓存?
        connection.setDoInput(true); //允许输出

        // use caller-provided custom SslSocketFactory, if any, for HTTPS
        if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
            ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
        }

        return connection;
    }
    //可以看到volley支持get,post,PUT等都支持的,符合http规范
    static void setConnectionParametersForRequest(HttpURLConnection connection,
            Request request) throws IOException, AuthFailureError {
        switch (request.getMethod()) {
            case Method.GET:
                // Not necessary to set the request method because connection defaults to GET but
                // being explicit here.
                connection.setRequestMethod("GET");
                break;
            case Method.POST:
                connection.setRequestMethod("POST");
                addBodyIfExists(connection, request);
            ...........
        }
    }
    private static void addBodyIfExists(HttpURLConnection connection, Request request)
            throws IOException, AuthFailureError {
        byte[] body = request.getBody();
        if (body != null) {
            connection.setDoOutput(true);
            connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType()); //request的getBodyContentType()方法可以设置Content-Type请求头
            DataOutputStream out = new DataOutputStream(connection.getOutputStream());
            out.write(body); //把getBody()返回的byte[]数组写入输入流,
            out.close();
        }
    }
    private static HttpEntity entityFromConnection(HttpURLConnection connection) {
        BasicHttpEntity entity = new BasicHttpEntity();
        InputStream inputStream;
        try {
            inputStream = connection.getInputStream();
        } catch (IOException ioe) {
            inputStream = connection.getErrorStream();
        }
        entity.setContent(inputStream); //把响应内容写入HttpEntity
        entity.setContentLength(connection.getContentLength());
        entity.setContentEncoding(connection.getContentEncoding());
        entity.setContentType(connection.getContentType());
        return entity;
    }

看到两者的区别了吧?
HttpClient使用起来很简单

  1. mClient.execute(httpRequest); 直接返回HttpResponse对象,这个对象封装了响应内容和响应头
  2. httpRequest.setHeader(key, headers.get(key)); 给请求添加头
  3. HttpEntity entity = new ByteArrayEntity(request.getBody()); httpRequest.setEntity(entity); //给请求添加post参数

HttpURLConnection使用麻烦多了
HttpResponse对象需要自己创建,并手动添加响应内容和响应头,这里创建的是BasicHttpResponse实现了HttpResponse接口

  1. connection.addRequestProperty(headerName, map.get(headerName)); //设置Http请求的head
  2. DataOutputStream out = new DataOutputStream(connection.getOutputStream()); out.write(request.getBody()); //给请求添加post参数
  3. response.addHeader(h); //调用HttpResponse的addHeader方法添加响应头
  4. response.setEntity(entityFromConnection(connection)); //调用HttpResponse的setEntity方法设置相应内容

你可能感兴趣的:(Android开发)