Android HTTP网络通信(一):使用HttpURLConnection、HttpClient

目前,大多数的Android应用程序都会使用HTTP协议来发送和接收网络数据,而Android中主要提供了两种方式来进行HTTP操作,HttpURLConnection和HttpClient。

GET和POST的对比

举个例子:get方式类似于明信片,只有请求头,没有请求体。而post方式类似于一封信,信封上的内容为请求头;信里面的内容为请求体(请求头和请求体是分开的)。

含义

  • GET:通过请求URI得到资源。一般用于获取/查询资源信息
  • POST:用于向服务器提交新的内容。一般用于更新资源信息

主要区别

  • get方式主要用于从服务器取回数据,post方式主要用于向服务器提交数据
  • get类似于明信片,只有请求头,没有请求体;post类似于一封信,信封上的内容为请求头;信里面的内容为请求体
  • 使用get方式向服务器提交的数据量较小,通常不超过2K,使用post方式向服务器提交的数据量通常没有限制(明信片不能多写,而写信可以写很多内容)
  • get请求是将所要提交的数据附在URL之后,而post请求是将提交的数据放置在请求体当中

HttpURLConnection

概念

HttpURLConnection是一种多用途、轻量极的HTTP客户端,使用它来进行HTTP操作可以适用于大多数的应用程序。虽然HttpURLConnection的API提供的比较简单,但是同时这也使得我们可以更加容易地去使用和扩展它。

更新历史

  • Android 2.2之前,HttpURLConnection一直存在着一些令人厌烦的bug。比如说对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效了。
  • Android 2.3,加入了更加透明化的响应压缩。HttpURLConnection会自动在每个发出的请求中加入如下消息头,并处理相应的返回结果:Accept-Encoding: gzip。还增加了一些HTTPS方面的改进,现在HttpsURLConnection会使用SNI(Server Name Indication)的方式进行连接,使得多个HTTPS主机可以共享同一个IP地址。除此之外,还增加了一些压缩和会话的机制。如果连接失败,它会自动去尝试重新进行连接。这使得HttpsURLConnection可以在不破坏老版本兼容性的前提下,更加高效地连接最新的服务器。
  • Android 4.0,添加了一些响应的缓存机制。通常我们就可以使用反射的方式来启动响应缓存功能,下面示例代码展示了如何在Android 4.0及以后的版本中去启用响应缓存的功能,同时还不会影响到之前的版本:
private void enableHttpResponseCache() {  
    try {  
        long httpCacheSize = 10 * 1024 * 1024; // 10 MiB  
        File httpCacheDir = new File(getCacheDir(), "http");  
        Class.forName("android.net.http.HttpResponseCache")  
            .getMethod("install", File.class, long.class)  
            .invoke(null, httpCacheDir, httpCacheSize);  
    } catch (Exception httpResponseCacheNotAvailable) {  
    }  
}

如何使用

  • 获取HttpURLConnection的实例(一般只需new出一个URL对象)并传入目标的网络地址,然后调用一下openConnection()方法即可,如下代码:
URL url = new URL("http://www.baidu.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  • 设置HTTP请求方式,常用的方式主要有两个,GET和POST。GET表示希望从服务器那里获取数据,而POST则表示希望提交数据给服务器。写法如下:
connection.setRequestMethod("GET");
  • 进行一些自由的定制,比如设置连接超时、读取超时的毫秒数,以及服务器希望得到的一些消息头等,这部分内容根据自己的实际情况进行编写,写法如下:
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
  • 调用getInputStream()方法获取到服务器返回的输入流,然后就是对输入流进行读取,如下所示:
InputStream in = connection.getInputStream();
  • 最后调用disconnect()方法将HTTP连接关闭掉,如下所示:
connection.disconnect();

实例操作

新建activity_httpurlconnection_httpclient.xml,如下代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/send_request_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="发送请求" />

</LinearLayout>

新建HttpURLConnectionOrHttpClientActivity,代码如下:

public class HttpURLConnectionOrHttpClientActivity extends Activity {
    private static final String TAG = "HttpURLConnectionOrHttpClientActivity";
    private Button mSendRequestButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_httpurlconnection_httpclient);
        mSendRequestButton = (Button) findViewById(R.id.send_request_button);
        mSendRequestButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                sendRequestWithHttpURLConnection();
            }
        });

    }

    private void sendRequestWithHttpURLConnection() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null; // 获取HttpURLConnection的实例
                try {
                    URL url = new URL("http://www.baidu.com");
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET"); // 设置HTTP请求方式
                    connection.setConnectTimeout(8000); // 自由的定制,设置连接超时、读取超时的毫秒数
                    connection.setReadTimeout(8000);
                    InputStream in = connection.getInputStream(); // 获取到服务器返回的输入流
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in)); // 对输入流进行读取
                    StringBuilder response = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        response.append(line);
                    }
                    Log.d(TAG, response.toString());

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (connection != null) { // 将HTTP连接关闭掉
                        connection.disconnect();
                    }
                }

            }
        }).start();
    }

}

可以看到,我们在按钮点击事件里调用了sendRequestWithHttpURLConnection()方法,在这个方法中先是开启了一个子线程,然后在子线程里使用HttpURLConnection发出一条HTTP请求,请求的目标地址就是百度的首页。接着利用BufferedReader对服务器返回的流进行读取,最终取出结果并以Log方式显示。运行程序,如下图:
这里写图片描述

如果想要提交数据给服务器,只需要将HTTP请求的方法改成POST,并在获取输入流之前把要提交的数据写出即可。注意每条数据都要以键值对的形式存在,数据与数据之间用&符号隔开,比如说我们想要向服务器提交用户名和密码,就可以这样写:

connection.setRequestMethod("POST");
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes("username=admin&password=123456");

HttpClient

概念

HttpClient是Apache提供的HTTP网络访问接口,从一开始的时候就被引入到了Android API中。DefaultHttpClient和AndroidHttpClient都是HttpClient具体的实现类,它们都拥有众多的API,而且实现比较稳定,bug数量也很少。但同时也由于HttpClient的API数量过多,使得我们很难在不破坏兼容性的情况下对它进行升级和扩展,所以目前Android团队在提升和优化HttpClient方面的工作态度并不积极。

如何使用

  • 创建一个DefaultHttpClient的实例

HttpClient是一个接口,因此无法创建它的实例,通常情况下都会创建一个DefaultHttpClient的实例,如下所示:

HttpClient httpClient = new DefaultHttpClient();
  • 发起请求,并传入目标的网络地址

发起一条GET请求,就可以创建一个HttpGet对象,并传入目标的网络地址,然后调用HttpClient的execute()方法即可:

HttpGet httpGet = new HttpGet("http://www.baidu.com");
httpClient.execute(httpGet);

如果是发起一条POST请求,需要创建一个HttpPost对象,并传入目标的网络地址,如下所示:

HttpPost httpPost = new HttpPost("http://www.baidu.com");

然后通过一个NameValuePair集合来存放待提交的参数,并将这个参数集合传入到一个UrlEncodedFormEntity中,然后调用HttpPost的setEntity()方法将构建好的UrlEncodedFormEntity传入,如下所示:

List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("username", "admin"));
params.add(new BasicNameValuePair("password", "123456"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "utf-8");
httpPost.setEntity(entity);
  • 调用HttpClient的execute()方法

HttpPost和HttpGet一样,调用HttpClient的execute()方法,并将HttpPost对象传入即可:

httpClient.execute(httpPost);

执行execute()方法之后会返回一个HttpResponse对象,服务器所返回的所有信息就会包含在这里面。通常情况下我们都会先取出服务器返回的状态码,如果等于200就说明请求和响应都成功了,如下所示:

if (httpResponse.getStatusLine().getStatusCode() == 200) {
    // 请求和响应都成功了
}

接下来在这个if判断的内部取出服务返回的具体内容,可以调用getEntity()方法获取到一个HttpEntity实例,然后再用EntityUtils.toString()这个静态方法将HttpEntity转换成字符串即可,如下所示:

HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity);

注意:如果服务器返回的数据是带有中文的,直接调用EntityUtils.toString()方法进行转换会有乱码的情况出现,这个时候只需要在转换的时候将字符集指定成utf-8就可以了,如下所示:

String response = EntityUtils.toString(entity, "utf-8");

选择HttpURLConnection还是HttpClient

在Android 2.2之前,HttpClient拥有较少的bug,因此建议使用它。

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

自定义HTTP通用类

新建Http通用类

新建HttpUtils ,包括HttpURLConnection和HttpClient分别以get、post方式提交数据,具体代码如下:

public class HttpUtils {
    private static final String TAG = "HttpUtils";

    public HttpUtils() {

    }

    /**
     * 以HttpURLConnection POST提交表单
     * 
     * @param path URL
     * @param params 填写的url的参数
     * @param encode 字节编码
     * @return
     */
    public static String sendPostRequest(String path, Map<String, String> params, String encode) {
        String result = "";
        StringBuffer buffer = new StringBuffer(); // 作为StringBuffer初始化的字符串
        HttpURLConnection urlConnection = null;
        try {
            if (params != null && !params.isEmpty()) {
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    buffer.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(), encode)).append("&");
                }
                buffer.deleteCharAt(buffer.length() - 1); // 删除掉最有一个&
            }
            Log.i(TAG, "提交的地址及参数--->>>" + String.valueOf(buffer.toString()));
            URL url = new URL(path);
            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setConnectTimeout(3000);
            urlConnection.setRequestMethod("POST"); // 设置HTTP请求方式
            urlConnection.setDoInput(true);
            urlConnection.setDoOutput(true);
            // 获得上传信息的字节大小以及长度
            byte[] mydata = buffer.toString().getBytes();
            // 表示设置请求体的类型是文本类型
            urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            urlConnection.setRequestProperty("Content-Length", String.valueOf(mydata.length));
            // 获得输出流,向服务器输出数据
            OutputStream outputStream = urlConnection.getOutputStream();
            outputStream.write(mydata, 0, mydata.length);
            outputStream.close();
            // 获得服务器响应的结果和状态码
            int responseCode = urlConnection.getResponseCode();
            Log.i(TAG, "状态码--->>>" + String.valueOf(responseCode));
            if (responseCode == 200) {
                result = changeInputStream(urlConnection.getInputStream(), encode);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
        }
        return result;
    }

    /**
     * 以HttpURLConnection GET方式请求
     * 
     * @param path URL
     * @param params 填写的url的参数
     * @param encode 字节编码
     * @return
     */
    public static String sendGetRequest(String path, Map<String, String> params, String encode) {
        String result = "";
        StringBuffer buffer = new StringBuffer(path);
        HttpURLConnection conn = null;

        if (params != null && !params.isEmpty()) {
            buffer.append('?');
            for (Map.Entry<String, String> entry : params.entrySet()) {
                try {
                    buffer.append(entry.getKey()).append('=').append(URLEncoder.encode(entry.getValue(), encode)).append('&');
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }
            buffer.deleteCharAt(buffer.length() - 1);
        }
        Log.i(TAG, "提交的地址及参数--->>>" + String.valueOf(buffer.toString()));

        try {
            URL url = new URL(buffer.toString());
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setReadTimeout(5000);
            conn.setConnectTimeout(5000);
            int responseCode = conn.getResponseCode();
            Log.i(TAG, "状态码--->>>" + String.valueOf(responseCode));
            if (responseCode == 200) {
                result = changeInputStream(conn.getInputStream(), encode);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
        return result;
    }

    /**
     * 以HttpClient POST方式提交表单
     * 
     * @param path URL
     * @param map 填写的url的参数
     * @param encode 字节编码
     * @return
     */
    public static String sendHttpClientPost(String path, Map<String, String> map, String encode) {
        String result = "";

        try {
            List<NameValuePair> list = new ArrayList<NameValuePair>();
            if (map != null && !map.isEmpty()) {
                for (Map.Entry<String, String> entry : map.entrySet()) {
                    list.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                }
            }

            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, encode); // 创建代表请求体的对象
            HttpPost httpPost = new HttpPost(path); // 生成使用POST方法的请求对象
            httpPost.setEntity(entity); // 将请求体放置在请求对象当中
            DefaultHttpClient client = new DefaultHttpClient();
            HttpResponse httpResponse = client.execute(httpPost); // 执行请求对象,获取服务器返还的相应对象
            int responseCode = httpResponse.getStatusLine().getStatusCode();
            Log.i(TAG, "状态码--->>>" + String.valueOf(responseCode));
            if (responseCode == 200) {
                result = changeInputStream(httpResponse.getEntity().getContent(), encode);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 以HttpClient Get方式请求
     * 
     * @param path URL
     * @param map 填写的url的参数
     * @param encode 字节编码
     * @return
     */
    public static String sendHttpClientGet(String path, Map<String, String> map, String encode) {
        String result = "";
        StringBuffer buffer = new StringBuffer(path);

        if (map != null && !map.isEmpty()) {
            buffer.append('?');
            for (Map.Entry<String, String> entry : map.entrySet()) {
                try {
                    buffer.append(entry.getKey()).append('=').append(URLEncoder.encode(entry.getValue(), encode)).append('&');
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }
            buffer.deleteCharAt(buffer.length() - 1);
        }
        HttpClient httpClient = new DefaultHttpClient(); // 创建HttpClient对象
        HttpGet httpGet = new HttpGet(buffer.toString()); // 创建代表请求的对象,参数是访问的服务器地址

        try {
            HttpResponse httpResponse = httpClient.execute(httpGet); // 执行请求,获取服务器发还的相应对象
            int responseCode = httpResponse.getStatusLine().getStatusCode();
            Log.i(TAG, "状态码--->>>" + String.valueOf(responseCode));
            if (responseCode == 200) {
                result = changeInputStream(httpResponse.getEntity().getContent(), encode);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 将一个输入流转换成指定编码的字符串
     * 
     * @param inputStream
     * @param encode
     * @return
     */
    private static String changeInputStream(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;
    }

}

新建Activity

我们直接修改HttpURLConnectionOrHttpClientActivity中的代码,如下:

public class HttpURLConnectionOrHttpClientActivity extends Activity {
    private static final String TAG = "HttpURLConnectionOrHttpClientActivity";
    private Button mSendRequestButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_httpurlconnection_httpclient);

        mSendRequestButton = (Button) findViewById(R.id.send_request_button);
        mSendRequestButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        String path = "http://192.168.111.118:8080/myhttp/servlet/LoginAction";
                        Map<String, String> params = new HashMap<String, String>();
                        params.put("username", "admin");
                        params.put("password", "123");
                        String result = HttpUtils.sendPostRequest(path, params, "UTF-8");
                        Log.d(TAG, result);
                    }
                }).start();
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

}

部署服务端程序

下载服务端程序:http://download.csdn.net/detail/dengchenhe/8525281
将下载的myhttp.war文件,部署在Apache Tomcat目录下。

运行APP程序

修改HttpURLConnectionOrHttpClientActivity如下部分:

String result = HttpUtils.sendPostRequest(path, params, "UTF-8");
  • HttpUtils.sendPostRequest:以HttpURLConnection POST提交
  • HttpUtils.sendGetRequest:以HttpURLConnection GET方式请求
  • HttpUtils.sendHttpClientPost:以HttpClient POST方式提交
  • HttpUtils.sendHttpClientGet:以HttpClient GET方式请求

这里写图片描述

这里写图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值