android 网络编程 - HttpURLConnection与HttpClient

安卓和Java应用开发少不了要提交HTTP请求,而基本上目前有两个实现方式:HttpUrlConnection(即URL.openConnection)和HttpClient。 HttpClient是apache的开源实现,而HttpUrlConnection是安卓标准实现。

1. HttpURLConnection

Android 2.2版本之前,HttpURLConnection一直存在着一些令人厌烦的bug。比如说对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效了。那么我们通常的解决办法就是直接禁用掉连接池的功能。所以在Android 2.2版本以及之前的版本使用HttpClient是较好的选择,而在Android 2.3版本及以后,HttpURLConnection则是最佳的选择,它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。另外在Android 6.0版本中,HttpClient库被移除了。

1.1 Get

首先需要获取到 HttpURLConnection 的实例,一般只需 new 出一个 URL 对象,并传入 目标的网络地址,然后调用一下 openConnection()方法即可。得到了 HttpURLConnection 的实例之后,我们可以设置一下 HTTP 请求所使用的方法。 常用的方法主要有两个,GET 和 POST。GET 表示希望从服务器那里获取数据,而 POST 则 表示希望提交数据给服务器。接下来就可以进行一些自由的定制了,比如设置连接超时、读取超时的毫秒数,以及服 务器希望得到的一些消息头等。这部分内容根据自己的实际情况进行编写。之后再调用 getInputStream()方法就可以获取到服务器返回的输入流了,剩下的任务就是 对输入流进行读取。接着利用 BufferedReader 对服务器 返回的流进行读取,并将结果存放到了一个 Message 对象中。这里为什么要使用 Message 对 象呢?当然是因为子线程中无法对 UI 进行操作了。我们希望可以将服务器返回的内容显示 到界面上,所以就创建了一个 Message 对象,并使用 Handler 将它发送出去。之后又在 Handler 的 handleMessage()方法中对这条 Message 进行处理。最后可以调用 disconnect()方法将这个 HTTP 连接关闭掉。


private void connectWithHttpURLConnection() {
    new Thread( new Runnable() {
        @Override
        public void run() {
            HttpURLConnection connection = null;
            try {
                // 调用URL对象的openConnection方法获取HttpURLConnection的实例
                URL url = new URL("http://www.cnblogs.com/gzdaijie");
                connection = (HttpURLConnection) url.openConnection();
                // 设置请求方式,GET或POST
                connection.setRequestMethod("GET");
                // 设置连接超时、读取超时的时间,单位为毫秒(ms)
                connection.setConnectTimeout(8000);
                connection.setReadTimeout(8000);
                // getInputStream方法获取服务器返回的输入流
                InputStream in = connection.getInputStream();
                // 使用BufferedReader对象读取返回的数据流
                // 按行读取,存储在StringBuider对象response中
                BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                StringBuilder response = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }
                Message message = new Message(); 
                message.what = SHOW_RESPONSE;
                // 将服务器返回的结果存放到Message中 
                message.obj = response.toString();
                handler.sendMessage(message);
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                if (connection != null){
                    // 结束后,关闭连接
                    connection.disconnect();
                }
            }
        }
    }).start();
}

1.2 Post

注意需要对中文进行设置编码格式

URLEncoder.encode(nickname.getText().toString(), “utf-8”)

public void send() {
        String target = "http://192.168.1.66:8081/blog/dealPost.jsp";   //要提交的目标地址
        URL url;
        try {
            url = new URL(target);
            HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); // 创建一个HTTP连接
            urlConn.setRequestMethod("POST"); // 指定使用POST请求方式
            urlConn.setDoInput(true); // 向连接中写入数据
            urlConn.setDoOutput(true); // 从连接中读取数据
            urlConn.setUseCaches(false); // 禁止缓存
            urlConn.setInstanceFollowRedirects(true);   //自动执行HTTP重定向

            DataOutputStream out = new DataOutputStream(urlConn.getOutputStream()); // 获取输出流
            String param = "nickname="
                    + URLEncoder.encode(nickname.getText().toString(), "utf-8")
                    + "&content="
                    + URLEncoder.encode(content.getText().toString(), "utf-8"); //连接要提交的数据
            out.writeBytes(param);//将要传递的数据写入数据输出流
            out.flush();    //输出缓存
            out.close();    //关闭数据输出流
            // 判断是否响应成功
            if (urlConn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                InputStreamReader in = new InputStreamReader(urlConn.getInputStream()); // 获得读取的内容
                BufferedReader buffer = new BufferedReader(in); // 获取输入流对象
                String inputLine = null;
                while ((inputLine = buffer.readLine()) != null) {
                    result += inputLine + "\n";
                }
                in.close(); //关闭字符输入流
            }
            urlConn.disconnect();   //断开连接
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

2. HttpClient

HttpClient是Apache提供的库,提供了高效的、最新的支持HTTP协议的工具包,封装了众多的http请求、响应等方法,但有个缺点就是太重量级了,API太多了。

2.1 Get

首先你需要知道,HttpClient 是一个接口,因此无法创建它的实例,通常情况下都会创 建一个 DefaultHttpClient 的实例。接下来如果想要发起一条 GET 请求,就可以创建一个 HttpGet 对象,并传入目标的网络 地址,然后调用 HttpClient 的 execute()方法即可。执行 execute()方法之后会返回一个 HttpResponse 对象,服务器所返回的所有信息就会包 含在这里面。通常情况下我们都会先取出服务器返回的状态码,如果等于 200 就说明请求和响应都成功了。接下来在这个 if 判断的内部取出服务返回的具体内容,可以调用 getEntity()方法获取到 一个 HttpEntity 实例,然后再用 EntityUtils.toString()这个静态方法将 HttpEntity 转换成字符串 即可。注意如果服务器返回的数据是带有中文的,直接调用 EntityUtils.toString()方法进行转换 会有乱码的情况出现,这个时候只需要在转换的时候将字符集指定成 utf-8 就可以了。

    // 创建DefaultHttpClient实例
    HttpClient httpClient = new DefaultHttpClient();      
    //传入网址,然后执行
    HttpGet httpGet = new HttpGet("http://www.cnblogs.com/gzdaijie");
    HttpResponse httpResponse = httpClient.execute(httpGet); 
    // 由状态码判断请求结果,
    // 常见状态码 200 请求成功,404 页面未找到,关于HTTP的更多状态码直接GOOGLE
    if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {       
        // 请求成功,使用HttpEntity获得返回数据
        // 使用EntityUtils将返回数据转换为字符串
        HttpEntity entity = httpResponse.getEntity(); 
        String response = EntityUtils.toString(entity);
        //如果是中文,指定编码 
        //==>String response = EntityUtils.toString(entity, "utf-8"); 

        //或者生成一个BufferedReader
        BufferedReader reader = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));
    }

2.2 Post

    HttpClient httpClient = new DefaultHttpClient();
    HttpPost httpPost("http://www.cnblogs.com/gzdaijie");  
    // 使用NameValuePair(键值对)存放参数
    List<NameValuePair> data = new ArrayList<NameValuePair>();
    // 添加键值对
    data.add(new BasicNameValuePair("stu_no", 12345));
    data.add(new BasicNameValuePair("stu_name", "Tom"));
    // 使用setEntity方法传入编码后的参数
    httpPost.setEntity(new UrlEncodedFormEntity(data, "utf-8")); 
    // 执行该POST请求
    HttpResponse httpResponse = httpClient.execute(httpPost);
    // .....省略处理httpResponse的代码,与GET方式一致 

3. 用回调函数实现网络编程

3.1 定义接口HttpCallbackListener,为了实现回调

在接口中定义了两个方法,onFinish()方法表示当服务器成功响应我们请 求的时候调用,onError()表示当进行网络操作出现错误的时候调用。这两个方法都带有参数, onFinish()方法中的参数代表着服务器返回的数据,而 onError()方法中的参数记录着错误的 详细信息。

// 定义HttpCallbackListener接口
// 包含两个方法,成功和失败的回调函数定义
public interface HttpCallbackListener {
    void onFinish(String response);
    void onError(Exception e);
}

3.2 定义类HttpUtil,为了网络助手类

先给 sendHttpRequest()方法添加了一个 HttpCallbackListener 参数,并在方法的内 部开启了一个子线程,然后在子线程里去执行具体的网络操作。注意子线程中是无法通过 return 语句来返回数据的,因此这里我们将服务器响应的数据传入了 HttpCallbackListener 的 onFinish()方法中,如果出现了异常就将异常原因传入到 onError()方法中。

/* 创建一个新的类 HttpTool,将公共的操作抽象出来
 * 为了避免调用sendHttpRequest方法时需实例化,设置为静态方法
 * 传入HttpCallbackListener对象为了方法回调
 * 因为网络请求比较耗时,一般在子线程中进行,
 * 为了获得服务器返回的数据,需要使用java的回调机制 */

public class HttpTool {
    public static void sendHttpRequest(final String address, final HttpCallbackListener listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;

                try {
                    URL url = new URL(address);
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    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);
                    }
                    if (listener != null) {
                        // 回调方法 onFinish()
                        listener.onFinish(response.toString());
                    }
                } catch (Exception e) {
                    if (listener != null) {
                        // 回调方法 onError()
                        listener.onError(e);
                    }
                } finally {
                    if (connection != null) {
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }
}  

3.3 调用网络助手类

这样的话,当服务器成功响应的时候我们就可以在 onFinish()方法里对响应数据进行处 理了,类似地,如果出现了异常,就可以在 onError()方法里对异常情况进行处理。如此一来, 我们就巧妙地利用回调机制将响应数据成功返回给调用方了。
另外需要注意的是,onFinish()方法和 onError()方法最终还是在子线程中运行的,因此 我们不可以在这里执行任何的 UI 操作,如果需要根据返回的结果来更新 UI,则仍然要使用异步消息处理机制(Handler)。

//使用该HttpTool发起GET请求
String url = "http://www.cnblogs.com/gzdaijie";
HttpTool.sendHttpRequest(url,new HttpCallbackListener(){
    @Override  
    public void onFinish(String response) {
        // ...省略对返回结果的处理代码  
    }  

    @Override  
    public void onError(Exception e) {   
        // ...省略请求失败的处理代码
    } 
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值