Java Android HTTP实现总结

 

Java Android HTTP实现总结

 

  Http(Hypertext Transfer Protocol)超文本传输协议,是一个基于请求/响应模式的无状态的协议,Http1.1版给出了持续连接的机制,客户端建立连接之后,可以发送多次请求,当不会再发送时再关闭连接。

 

  Android使用Java,对于Http协议的基本功能有两种实现方案:

  1.使用JDK的java.net包下的HttpURLConnection.

  2.使用Apache的HttpClient

  关于二者的比较可以看一下:

  http://www.cnblogs.com/devinzhang/archive/2012/01/17/2325092.html

 

  Android SDK中集成了Apache的HttpClient模块,也即说Android上两种方法都能用。

  之前看一个Android开发者博客(原文链接先空缺,需要FQ)对此的讨论,大意总结如下:

  1.HttpClient的功能比较全,更加强大;而HttpURLConnection的功能较简单和原始,但是性能更好。

  2.在Android 2.x的版本中使用HttpURLConnection有bug,但是后来高级版本的Android已经将带来的bug修复,并且做了一些进一步优化的工作,所以建议在高级版本的Android系统(Android 2.3之后)使用HttpURLConnection,低版本的系统仍使用HttpClient。

 

程序实现

  下面来讨论一下实现,首先,需要确认Manifest中有权限:

    <uses-permission android:name="android.permission.INTERNET" />

 

使用JDK的HttpURLConnection类

  HttpURLConnection参考:

  http://developer.android.com/reference/java/net/HttpURLConnection.html

  使用这个类的一般步骤:

  1.通过URL.openConnection() 方法获取一个HttpURLConnection对象,并且将结果强制转化为HttpURLConnection类型。

  2.准备请求(prepare the request),包括URI,headers中的各种属性等

  (Request headers may also include metadata such as credentials, preferred content types, and session cookies.)

  3.请求体(optionally)。如果有请求体那么setDoOutput(true)必须为true,然后把输入放在getOutputStream()流中。

  4.读取响应。响应的headers一般包括了一些metadata比如响应体的内容类型和长度,修改日期以及session cookies。响应体可以从 getInputStream()流中读出。

  5.断开连接。响应体被读出之后,应该调用 disconnect()方法来断开连接。

  例子代码:

Java Android HTTP实现总结
package com.example.helloandroidhttp;



import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.UnsupportedEncodingException;

import java.net.HttpURLConnection;

import java.net.MalformedURLException;

import java.net.URL;

import java.net.URLEncoder;

import java.util.Map;



import android.util.Log;



public class HttpUtilsJDK {

    private static final String LOG_TAG = "Http->JDK";

    private static final int CONNECT_TIME_OUT = 3000;

    private static final String HEADER_CONTENT_TYPE = "Content-Type";

    private static final String HEADER_CONTENT_LENGTH = "Content-Length";

    /**

     * Default encoding for POST or PUT parameters. See

     * {@link #getParamsEncoding()}.

     */

    private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";



    public static String getParamsEncoding() {

        return DEFAULT_PARAMS_ENCODING;

    }



    public static String getBodyContentType() {

        return "application/x-www-form-urlencoded; charset="

                + getParamsEncoding();

    }



    public static String performGetRequest(String baseUrl) {

        String result = null;

        HttpURLConnection connection = null;

        try {

            URL url = new URL(baseUrl);

            if (null != url) {



                // 获取HttpURLConnection类型的对象

                connection = (HttpURLConnection) url.openConnection();

                // 设置连接的最大等待时间

                connection.setConnectTimeout(CONNECT_TIME_OUT);



                // Sets the maximum time to wait for an input stream read to

                // complete before giving up.

                connection.setReadTimeout(3000);

                // 设置为GET方法

                connection.setRequestMethod("GET");

                connection.setDoInput(true);



                if (200 == connection.getResponseCode()) {

                    InputStream inputStream = connection.getInputStream();



                    result = getResultString(inputStream, getParamsEncoding());

                }

                else {

                    Log.e(LOG_TAG,

                            "Connection failed: "

                                    + connection.getResponseCode());

                }



            }

        }

        catch (MalformedURLException e) {

            e.printStackTrace();

        }

        catch (IOException e) {

            e.printStackTrace();

        }

        finally {

            connection.disconnect();

        }



        return result;

    }



    public static String performPostRequest(String baseUrl,

            Map<String, String> params) {

        String result = null;

        HttpURLConnection connection = null;

        try {

            URL url = new URL(baseUrl);

            if (null != url) {

                // 获取HttpURLConnection类型的对象

                connection = (HttpURLConnection) url.openConnection();

                // 设置响应超时限制

                connection.setConnectTimeout(CONNECT_TIME_OUT);

                // 设置为POST方法

                connection.setRequestMethod("POST");

                connection.setDoInput(true);

                // 有请求体则setDoOutput(true)必须设定

                connection.setDoOutput(true);



                // 为了性能考虑,如果包含请求体,那么最好调用 setFixedLengthStreamingMode(int)或者

                // setChunkedStreamingMode(int)

                // connection.setChunkedStreamingMode(0);// 参数为0时使用默认值



                byte[] data = getParamsData(params);



                connection.setRequestProperty(HEADER_CONTENT_TYPE,

                        getBodyContentType());

                if (null != data) {

                    connection.setFixedLengthStreamingMode(data.length);

                    connection.setRequestProperty(HEADER_CONTENT_LENGTH,

                            String.valueOf(data.length));

                    OutputStream outputStream = connection.getOutputStream();

                    outputStream.write(data);

                }



                // 得到返回值

                int responseCode = connection.getResponseCode();

                if (200 == responseCode) {

                    result = getResultString(connection.getInputStream(),

                            getParamsEncoding());



                }

                else {

                    Log.e(LOG_TAG,

                            "Connection failed: "

                                    + connection.getResponseCode());

                }



            }

        }

        catch (MalformedURLException e) {

            e.printStackTrace();

        }

        catch (IOException e) {

            e.printStackTrace();

        }

        finally {

            connection.disconnect();

        }



        return result;

    }



    private static byte[] getParamsData(Map<String, String> params) {

        byte[] data = null;



        try {

            if (null != params && !params.isEmpty()) {

                StringBuffer buffer = new StringBuffer();



                for (Map.Entry<String, String> entry : params.entrySet()) {



                    buffer.append(entry.getKey())

                            .append("=")

                            .append(URLEncoder.encode(entry.getValue(),

                                    getParamsEncoding())).append("&");// 请求的参数之间使用&分割。



                }

                // 最后一个&要去掉

                buffer.deleteCharAt(buffer.length() - 1);



                data = buffer.toString().getBytes(getParamsEncoding());

            }

        }

        catch (UnsupportedEncodingException e) {

            e.printStackTrace();



        }



        return data;

    }



    private static String getResultString(InputStream inputStream, String encode) {

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        byte[] data = new byte[1024];

        int len = 0;

        String result = "";

        if (inputStream != null) {

            try {

                while ((len = inputStream.read(data)) != -1) {

                    outputStream.write(data, 0, len);

                }

                result = new String(outputStream.toByteArray(), encode);



            }

            catch (IOException e) {

                e.printStackTrace();

            }

        }

        return result;

    }

}
HttpUtilsJDK

 

使用Apache的HttpClient

  可以查看官方的Tutorial:

  http://hc.apache.org/httpcomponents-client-ga/tutorial/html/index.html

  Android有一个实现类AndroidHttpClient,实现了HttpClient

  http://developer.android.com/reference/android/net/http/AndroidHttpClient.html

  包装了一些默认的设置。

 

关于HTTP entity

  HTTP消息中可以包含内容实体(content entity),可以看做消息的报文,包含在请求或者响应中。

  HTTP规范规定两种请求方法可以包含内容实体:POST和PUT。

  响应则通常是包含内容实体的。

 

  HttpClient会根据内容来源区分三种实体:

  1.streamed:内容来源是流,这类里包含了从HTTP响应中获得的实体,流式实体不可重复。

  2.self-contained:内容是从内存或者其他方式获得的,即和连接无关,这类实体是可以重复的,多数是用来放在HTTP请求中的实体。

  3.wrapping:这类实体是从其他实体获得的。

  对于用HttpClient创建的请求实体来说,streamed和self-contained类型的区别其实不太重要,建议把不可重复的实体看作是streamed的,可重复的看作是self-contained的。

 

创造实体内容

  为了发送HTTP的POST请求(当然还有PUT请求也有实体),需要把一些参数放在实体中,创造实体内容,有四个类型的类可选用:

  StringEntity, ByteArrayEntity, InputStreamEntity, FileEntity

  注意其中的InputStreamEntity是不可重复的。

 

  UrlEncodedFormEntity这个类是用来把输入数据编码成合适的内容,比如下面这段:

List<NameValuePair> formparams = new ArrayList<NameValuePair>();

formparams.add(new BasicNameValuePair("param1", "value1"));

formparams.add(new BasicNameValuePair("param2", "value2"));

UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);

HttpPost httppost = new HttpPost("http://localhost/handler.do");

httppost.setEntity(entity);

 

  两个键值对,被UrlEncodedFormEntity实例编码后变为如下内容:

param1=value1&param2=value2

  

  使用Apache的HttpClient发送HTTP请求的辅助类,例子代码:

Java Android HTTP实现总结
package com.example.helloandroidhttp;



import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.UnsupportedEncodingException;

import java.net.URLEncoder;

import java.util.ArrayList;

import java.util.List;

import java.util.Map;



import org.apache.http.HttpEntity;

import org.apache.http.HttpResponse;

import org.apache.http.NameValuePair;

import org.apache.http.client.HttpClient;

import org.apache.http.client.entity.UrlEncodedFormEntity;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.client.methods.HttpPost;

import org.apache.http.entity.ByteArrayEntity;

import org.apache.http.entity.StringEntity;

import org.apache.http.impl.client.DefaultHttpClient;

import org.apache.http.message.BasicNameValuePair;

import org.apache.http.params.BasicHttpParams;

import org.apache.http.params.HttpConnectionParams;

import org.apache.http.params.HttpParams;



import android.util.Log;



public class HttpUtilsApache {



    private static final String LOG_TAG = "Http->Apache";

    private static final String HEADER_CONTENT_TYPE = "Content-Type";

    /**

     * Default encoding for POST or PUT parameters. See

     * {@link #getParamsEncoding()}.

     */

    private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";



    /**

     * Returns which encoding should be used when converting POST or PUT

     * parameters returned by {@link #getParams()} into a raw POST or PUT body.

     *

     * <p>

     * This controls both encodings:

     * <ol>

     * <li>The string encoding used when converting parameter names and values

     * into bytes prior to URL encoding them.</li>

     * <li>The string encoding used when converting the URL encoded parameters

     * into a raw byte array.</li>

     * </ol>

     */

    public static String getParamsEncoding() {

        return DEFAULT_PARAMS_ENCODING;

    }



    public static String getBodyContentType() {

        return "application/x-www-form-urlencoded; charset="

                + getParamsEncoding();

    }



    public static String performGetRequest(String url) {



        String result = null;

        // 生成一个请求对象

        HttpGet httpGet = new HttpGet(url);



        // 1.生成一个Http客户端对象(带参数的)

        HttpParams httpParameters = new BasicHttpParams();

        HttpConnectionParams.setConnectionTimeout(httpParameters, 10 * 1000);// 设置请求超时10秒

        HttpConnectionParams.setSoTimeout(httpParameters, 10 * 1000); // 设置等待数据超时10秒

        HttpConnectionParams.setSocketBufferSize(httpParameters, 8192);



        HttpClient httpClient = new DefaultHttpClient(httpParameters); // 此时构造DefaultHttpClient时将参数传入

        // 2.默认实现:

        // HttpClient httpClient = new DefaultHttpClient();

        httpGet.addHeader(HEADER_CONTENT_TYPE, getBodyContentType());



        // 下面使用Http客户端发送请求,并获取响应内容



        HttpResponse httpResponse = null;



        try {

            // 发送请求并获得响应对象

            httpResponse = httpClient.execute(httpGet);



            final int statusCode = httpResponse.getStatusLine().getStatusCode();

            if (200 == statusCode) {

                result = getResponseString(httpResponse);

            }

            else {

                Log.e(LOG_TAG, "Connection failed: " + statusCode);

            }



        }

        catch (Exception e) {

            e.printStackTrace();

        }

        finally {



        }



        return result;

    }



    public static String performPostRequest(String baseURL, String postData) {

        String result = "";

        HttpResponse response = null;

        try {



            // URL使用基本URL即可,其中不需要加参数

            HttpPost httpPost = new HttpPost(baseURL);

            // 设置ContentType

            httpPost.addHeader(HEADER_CONTENT_TYPE, getBodyContentType());



            // 将请求体内容加入请求中

            HttpEntity requestHttpEntity = prepareHttpEntity(postData);



            if (null != requestHttpEntity) {

                httpPost.setEntity(requestHttpEntity);

            }



            // 需要客户端对象来发送请求

            HttpClient httpClient = new DefaultHttpClient();

            // 发送请求

            response = httpClient.execute(httpPost);



            final int statusCode = response.getStatusLine().getStatusCode();

            if (200 == statusCode) {

                // 显示响应

                result = getResponseString(response);

            }

            else {

                Log.e(LOG_TAG, "Connection failed: " + statusCode);

            }



        }

        catch (Exception e) {

            e.printStackTrace();

        }

        finally {



        }



        return result;



    }



    /**

     * 直接利用String生成HttpEntity,String应该已经是key=value&key2=value2的形式

     *

     * @param postData

     * @return

     */

    private static HttpEntity prepareHttpEntity(String postData) {



        HttpEntity requestHttpEntity = null;



        try {



            if (null != postData) {

                // 去掉所有的换行

                postData = postData.replace("\n", "");

                // one way

                // requestHttpEntity = new ByteArrayEntity(

                // postData.getBytes(getParamsEncoding()));



                // another way

                requestHttpEntity = new StringEntity(postData,

                        getParamsEncoding());

                ((StringEntity) requestHttpEntity)

                        .setContentEncoding(getParamsEncoding());

                ((StringEntity) requestHttpEntity)

                        .setContentType(getBodyContentType());



            }

        }

        catch (Exception e) {

            e.printStackTrace();

        }

        return requestHttpEntity;

    }



    /**

     * 利用Map结构的参数生成HttpEntity,使用UrlEncodedFormEntity对参数对进行编码

     *

     * @param params

     * @return

     */

    private static HttpEntity prepareHttpEntity1(Map<String, String> params) {

        // 需要将String里面的key value拆分出来



        HttpEntity requestHttpEntity = null;

        try {



            if (null != params) {

                List<NameValuePair> pairList = new ArrayList<NameValuePair>(

                        params.size());

                for (Map.Entry<String, String> entry : params.entrySet()) {

                    NameValuePair pair = new BasicNameValuePair(entry.getKey(),

                            entry.getValue());

                    pairList.add(pair);

                }

                requestHttpEntity = new UrlEncodedFormEntity(pairList,

                        getParamsEncoding());



            }



        }

        catch (UnsupportedEncodingException e) {

            e.printStackTrace();

        }



        return requestHttpEntity;

    }



    /**

     * 利用Map结构的参数生成HttpEntity,使用自己的方法对参数进行编码合成字符串

     *

     * @param params

     * @return

     */

    private static HttpEntity prepareHttpEntity2(Map<String, String> params) {

        // 需要将String里面的key value拆分出来



        HttpEntity requestHttpEntity = null;

        byte[] body = encodeParameters(params, getParamsEncoding());

        requestHttpEntity = new ByteArrayEntity(body);



        return requestHttpEntity;

    }



    /**

     * Converts <code>params</code> into an application/x-www-form-urlencoded

     * encoded string.

     */

    private static byte[] encodeParameters(Map<String, String> params,

            String paramsEncoding) {

        StringBuilder encodedParams = new StringBuilder();

        try {

            for (Map.Entry<String, String> entry : params.entrySet()) {

                encodedParams.append(URLEncoder.encode(entry.getKey(),

                        paramsEncoding));

                encodedParams.append('=');

                encodedParams.append(URLEncoder.encode(entry.getValue(),

                        paramsEncoding));

                encodedParams.append('&');

            }

            return encodedParams.toString().getBytes(paramsEncoding);

        }

        catch (UnsupportedEncodingException uee) {

            throw new RuntimeException("Encoding not supported: "

                    + paramsEncoding, uee);

        }

    }



    public static String getResponseString(HttpResponse response) {

        String result = null;

        if (null == response) {

            return result;

        }



        HttpEntity httpEntity = response.getEntity();

        InputStream inputStream = null;

        try {

            inputStream = httpEntity.getContent();

            BufferedReader reader = new BufferedReader(new InputStreamReader(

                    inputStream));

            result = "";

            String line = "";

            while (null != (line = reader.readLine())) {

                result += line;

            }

        }

        catch (Exception e) {

            e.printStackTrace();

        }

        finally {

            try {

                if (null != inputStream) {

                    inputStream.close();

                }

            }

            catch (IOException e) {

                e.printStackTrace();

            }

        }

        return result;



    }

}
HttpUtilsApache

 

参考资料

  本博客HTTP标签下相关文章,比如这个:

  http://www.cnblogs.com/mengdd/p/3144599.html

  Apache HttpClient:

  http://hc.apache.org/httpcomponents-client-ga/

  http://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html#d5e49

 

  Android之网络编程 系列博文:

  http://www.cnblogs.com/devinzhang/category/349642.html

 

  Http Header详解:

  http://kb.cnblogs.com/page/92320/

  Android--Apache HttpClient:

  http://www.cnblogs.com/plokmju/p/Android_apacheHttpClient.html

 

推荐项目

  Android网络通信框架Volley:

  https://github.com/mengdd/android-volley

  Android Asynchronous Http Client:A Callback-Based Http Client Library for Android

  https://github.com/mengdd/android-async-http

  也即:http://loopj.com/android-async-http/

  本文项目地址(目前还是个挺简陋的Demo,有待完善):

  https://github.com/mengdd/HelloAndroidHttpUtils

 

你可能感兴趣的:(android)