• 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(这个需要翻墙):

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

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

     

     

     

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

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

  • 相关阅读:
    【转】关于char * 与 char[]
    网页打印js代码
    无法用排他锁锁定该数据库,以执行该操作。 (Microsoft SQL Server,错误: 5030)
    CKEditor使用笔记
    FormView作为单独编辑页笔记
    用WindowsMediaPlayer控件写个WinForm播放器
    ListView搭配DataPager控件实现分页笔记
    如何禁用ViewState
    C#获取本机IP搜集整理7种方法
    ListView高效率分页笔记
  • 原文地址:https://www.cnblogs.com/mingfeng002/p/3492874.html
Copyright © 2020-2023  润新知