Android的网络通信

Android平台有三种网络接口可以使用,他们分别是:java.net.*(标准Java接口)、Org.apache接口和Android.net.*(Android网络接口)。大多数的Android应用程序都会使用HTTP协议来发送和接收网络数据,而Android中主要提供了两种方式来进行HTTP操作,HttpURLConnection和HttpClient。这两种方式都支持HTTPS协议、以流的形式进行上传和下载、配置超时时间、IPv6、以及连接池等功能。
1.标准Java接口
java.net.*提供与联网有关的类,包括流、数据包套接字(socket)、Internet协议、常见Http处理等。比如:创建URL,以及URLConnection/HttpURLConnection对象、设置链接参数、链接到服务器、向服务器写数据、从服务器读取数据等通信。这些在Java网络编程中均有涉及,我们看一个简单的socket编程,实现服务器回发客户端信息。

java.net包中的HttpURLConnection类

 开发文档描述http://developer.android.com/reference/java/net/HttpURLConnection.html

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

下面看看一些存在问题

 1.在Android 2.2版本之前,HttpURLConnection一直存在着一些令人厌烦的bug。比如说对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效了。那么我们通常的解决办法就是直接禁用掉连接池的功能:

private void disableConnectionReuseIfNecessary() {
    // 这是一个2.2版本之前的bug
    if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
        System.setProperty("http.keepAlive", "false");
    }
}

2.HttpURLConnection.getContentLength()大小问题

HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.connect();
int length = conn.getContentLength();
InputStream is = conn.getInputStream();

 在Android 2.2版本之前,如果服务端没有设content length,conn.getContentLength() 时获取到的值为 -1。

 

3.gzip压缩文件大小改变问题

在Android 2.3版本的时候,更加透明化的响应压缩。HttpURLConnection会自动在每个发出的请求中加入如下消息头,并处理相应的返回结果:Accept-Encoding: gzip。使用 gzip方式压缩,HTTP响应头里的Content-Length就会代表着压缩后的长度,这时再使用getContentLength()方法来取出解压后的数据就是错误的了,如果这个是需要做下载进度的话可能会出问题,可以加上这句话来要求http请求不要gzip压缩

 urlConnection.setRequestProperty("Accept-Encoding", "identity");

 我们在Android 2.3版本中还增加了一些HTTPS方面的改进,现在HttpsURLConnection会使用SNI(Server Name Indication)的方式进行连接,使得多个HTTPS主机可以共享同一个IP地址。除此之外,还增加了一些压缩和会话的机制。如果连接失败,它会自动去尝试重新进行连接。这使得HttpsURLConnection可以在不破坏老版本兼容性的前提下,更加高效地连接最新的服务器。

4.HttpURLConnection上传大的文件的时候会容易内存溢出

在这片文章里面有介绍http://www.mzone.cc/article/198.html

5.链接超时问题

HttpURLConnection是基于HTTP协议的,其底层通过socket通信实现。如果不设置超时(timeout),在网络异常的情况下,可能会导致程序僵死而不继续往下执行。可以使用HttpURLConnection的父类URLConnection的以下两个方法:
setConnectTimeout:设置连接主机超时(单位:毫秒)
setReadTimeout:设置从主机读取数据超时(单位:毫秒)

HttpURLConnection urlCon = (HttpURLConnection)url.openConnection();
urlCon.setConnectTimeout(30000);
urlCon.setReadTimeout(30000);

 

新增功能

 1。在Android 4.0版本中,我们又添加了一些响应的缓存机制。

在Ice Cream Sandwich(4.0),增加了response cache。安装了cache后,当缓存被安装后(调用HttpResponseCache的install()方法),所有的HTTP请求都会满足以下三种情况:

  •  所有的缓存响应都由本地存储来提供。因为没有必要去发起任务的网络连接请求,所有的响应都可以立刻获取到。
  •  视情况而定的缓存响应必须要有服务器来进行更新检查。比如说客户端发起了一条类似于 “如果/foo.png这张图片发生了改变,就将它发送给我” 这样的请求,服务器需要将更新后的数据进行返回,或者返回一个304 Not Modified状态。如果请求的内容没有发生,客户端就不会下载任何数据。
  •  没有缓存的响应都是由服务器直接提供的。这部分响应会在稍后存储到响应缓存中。

 由于这个功能是在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) {
    }
}

 你也应该同时配置一下你的Web服务器,在HTTP响应上加入缓存的消息头。

 下面看看使用方法

Get方式:

public static void requestByGet() throws Exception {  
    String path = "https://reg.163.com/logins.jsp?id=helloworld&pwd=android";  
    // 新建一个URL对象  
    URL url = new URL(path);  
    // 打开一个HttpURLConnection连接  
    HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();  
    // 设置连接超时时间  
    urlConn.setConnectTimeout(5 * 1000);  
    // 开始连接  
    urlConn.connect();  
    // 判断请求是否成功  
    if (urlConn.getResponseCode() == HTTP_200) {  
        // 获取返回的数据  
        byte[] data = readStream(urlConn.getInputStream());  
        Log.i(TAG_GET, "Get方式请求成功,返回数据如下:");  
        Log.i(TAG_GET, new String(data, "UTF-8"));  
    } else {  
        Log.i(TAG_GET, "Get方式请求失败");  
    }  
    // 关闭连接  
    urlConn.disconnect();  
}

Post方式:

// Post方式请求  
public static void requestByPost() throws Throwable {  
    String path = "https://reg.163.com/logins.jsp";  
    // 请求的参数转换为byte数组  
    String params = "id=" + URLEncoder.encode("helloworld", "UTF-8")  
            + "&pwd=" + URLEncoder.encode("android", "UTF-8");  
    byte[] postData = params.getBytes();  
    // 新建一个URL对象  
    URL url = new URL(path);  
    // 打开一个HttpURLConnection连接  
    HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();  
    // 设置连接超时时间  
    urlConn.setConnectTimeout(5 * 1000);  
    // Post请求必须设置允许输出  
    urlConn.setDoOutput(true);  
    // Post请求不能使用缓存  
    urlConn.setUseCaches(false);  
    // 设置为Post请求  
    urlConn.setRequestMethod("POST");  
    urlConn.setInstanceFollowRedirects(true);  
    // 配置请求Content-Type  
    urlConn.setRequestProperty("Content-Type",  
            "application/x-www-form-urlencode");  
    // 开始连接  
    urlConn.connect();  
    // 发送请求参数  
    DataOutputStream dos = new DataOutputStream(urlConn.getOutputStream());  
    dos.write(postData);  
    dos.flush();  
    dos.close();  
    // 判断请求是否成功  
    if (urlConn.getResponseCode() == HTTP_200) {  
        // 获取返回的数据  
        byte[] data = readStream(urlConn.getInputStream());  
        Log.i(TAG_POST, "Post请求方式成功,返回数据如下:");  
        Log.i(TAG_POST, new String(data, "UTF-8"));  
    } else {  
        Log.i(TAG_POST, "Post方式请求失败");  
    }  
}

 2.Apache接口org.apache.http包中的HttpGet和HttpPost类
对于大部分应用程序而言JDK本身提供的网络功能已远远不够,这时就需要Android提供的Apache HttpClient了。它是一个开源项目,功能更加完善,为客户端的Http编程提供高效、最新、功能丰富的工具包支持。
在android SDK中httpclient使用的是4.0beta2,我不得不说这个版本里面有些蛋疼的bug:在4.0上的sdk,将wifi和3g同时打开,理论上来说,网络接口应该走wifi,但是却走了代理,导致访问服务器网络失败;

解决上面问题的办法就是引入“http://code.google.com/p/httpclientandroidlib/”中的库,然后修改相应的类,典型的例子就是ThreadSafeClientConnManager变成了PoolingClientConnectionManager。 

下面看看使用方法

Get方式:

// HttpGet方式请求  
public static void requestByHttpGet() throws Exception {  
    String path = "https://reg.163.com/logins.jsp?id=helloworld&pwd=android";  
    // 新建HttpGet对象  
    HttpGet httpGet = new HttpGet(path);  
    // 获取HttpClient对象  
    HttpClient httpClient = new DefaultHttpClient();  
    // 获取HttpResponse实例  
    HttpResponse httpResp = httpClient.execute(httpGet);  
    // 判断是够请求成功  
    if (httpResp.getStatusLine().getStatusCode() == HTTP_200) {  
        // 获取返回的数据  
        String result = EntityUtils.toString(httpResp.getEntity(), "UTF-8");  
        Log.i(TAG_HTTPGET, "HttpGet方式请求成功,返回数据如下:");  
        Log.i(TAG_HTTPGET, result);  
    } else {  
        Log.i(TAG_HTTPGET, "HttpGet方式请求失败");  
    }  
} 

 Post方式:

// HttpPost方式请求  
public static void requestByHttpPost() throws Exception {  
    String path = "https://reg.163.com/logins.jsp";  
    // 新建HttpPost对象  
    HttpPost httpPost = new HttpPost(path);  
    // Post参数  
    List<NameValuePair> params = new ArrayList<NameValuePair>();  
    params.add(new BasicNameValuePair("id", "helloworld"));  
    params.add(new BasicNameValuePair("pwd", "android"));  
    // 设置字符集  
    HttpEntity entity = new UrlEncodedFormEntity(params, HTTP.UTF_8);  
    // 设置参数实体  
    httpPost.setEntity(entity);  
    // 获取HttpClient对象  
    HttpClient httpClient = new DefaultHttpClient();  
    // 获取HttpResponse实例  
    HttpResponse httpResp = httpClient.execute(httpPost);  
    // 判断是够请求成功  
    if (httpResp.getStatusLine().getStatusCode() == HTTP_200) {  
        // 获取返回的数据  
        String result = EntityUtils.toString(httpResp.getEntity(), "UTF-8");  
        Log.i(TAG_HTTPGET, "HttpPost方式请求成功,返回数据如下:");  
        Log.i(TAG_HTTPGET, result);  
    } else {  
        Log.i(TAG_HTTPGET, "HttpPost方式请求失败");  
    }  
} 

 

 关于安卓HTTP请求用HttpUrlConnection还是HttpClient好

 

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

网上不少人都认为HttpClient更好,理由是功能更强,BUG更少,更容易控制细节。但我个人认为普通JAVA人员可选用HttpClient,安卓开发人员则应该使用HttpUrlConnection,理由如下:

1.HttpClient是apache的开源实现,而HttpUrlConnection是安卓标准实现,安卓SDK虽然集成了HttpClient,但官方支持的却是HttpUrlConnection;

2. 2.3版本HttpUrlConnection直接支持GZIP压缩;HttpClient也支持,但要自己写代码处理;我们之前测试HttpUrlConnection的GZIP压缩在传大文件分包trunk时有问题,只适合小文件,不过这个BUG后来官方说已经修复了;

3.HttpUrlConnection直接支持系统级连接池,即打开的连接不会直接关闭,在一段时间内所有程序可共用;HttpClient当然也能做到,但毕竟不如官方直接系统底层支持好;

4.在4.0版本中HttpUrlConnection直接在系统层面做了缓存策略处理,加快重复请求的速度。

 

谷歌自己也是推荐用HttpUrlConnection,对它进行了大量的优化,这个从安卓的帮助文档可以看出来:

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

安卓开发博客上也强调,2.2以后的安卓都应该用HttpUrlConnection(这个需要FQ):

http://android-developers.blogspot.com/2011/09/androids-http-clients.html

Android的网络通信

总之,在安卓开发上,虽然HttpClient更好地支持很多细节的控制(如代理、COOKIE、鉴权、压缩、连接池),但相应地对开发人员要求更高,代码写起来更复杂,普通开发人员很难做到对它很好地驾驭,官方的支持也越来越少;而HttpUrlConnection对大部分工作进行了包装,屏蔽了不需要的细节,更适合开发人员直接调用,而且官方对它的支持和优化也会越来越好。我们既然是做安卓应用的开发,自然要遵循安卓官方的指引,选用HttpUrlConnection。

 

 

 

 

参考http://blog.csdn.net/guolin_blog/article/details/12452307

     http://blog.csdn.net/huzgd/article/details/8712187

 

Android的网络通信,布布扣,bubuko.com

Android的网络通信

上一篇:深入浅出游戏算法(4)-unity3d算法(2)-移动球


下一篇:Android开源项目第三篇——优秀项目篇