前言:

**
恍惚间,从事Android开发已经有半年有余了!之前一直在沉浸于项目中的Bug解决,颇有种一叶障目不见泰山的感觉,对Android网络通信这块儿缺乏一种宏观的认识,适逢一个难得的阴雨周末,躺着也是无聊,倒不如写写耍耍来的痛快!

Android端的开发,除了那些没有植入广告的单机版小游戏小应用,几乎大部分都要牵涉到网络数据的请求。当前Android端的网络编程可以分为两种:一种是通过Socket方式,另外一种则是基于http协议。这两种访问方式大不相同,Socket方式是面向TCP/UDP 协议的,而HTTP通信则是借助于Http协议,这两者分别建立在ISO 七层模型的传输层协议和应用层协议之上。(关于Android中的Socket和http区别,可以查看我的另外一篇文章—–《Android之Http,Socket和Tcp/Ip》)。

Socket通信在Android中应用不多,在桌面程序中倒是应用广泛如众所周知的QQ,关于Socket这篇文章不准备展开,主旨在于总结归纳一下Android 的HTTP通信方式。

一.HTTP通信详细归纳

1.1 http网络连接实现方式可以细分为两类实现:

1.借助于Java核心包 java.net中的HttpURLConnection 类

2.借助于Apache开源组织提供的HttpClient类。
org.apache.http.client.HttpClient;

1.2 HttpURLConnection和HttpClient的区别和联系

HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。在 JDK 的 java.net 包中已经提供了访问 HTTP 协议的基本功能:
1.2.1 HttpURLConnection

HttpURLConnection是java的标准类,HttpURLConnection继承自URLConnection,可用于向指定网站发送GET请求、POST请求。它在URLConnection的基础上提供了如下便捷的方法:

int getResponseCode():获取服务器的响应代码。
String getResponseMessage():获取服务器的响应消息。
String getResponseMethod():获取发送请求的方法。
void setRequestMethod(String method):设置发送请求的方法。

在一般情况下,如果只是需要Web站点的某个简单页面提交请求并获取服务器响应,HttpURLConnection完全可以胜任。但在绝大部分情况下,Web站点的网页可能没这么简单,这些页面并不是通过一个简单的URL就可访问的,可能需要用户登录而且具有相应的权限才可访问该页面。在这种情况下,就需要涉及Session、Cookie的处理了,如果打算使用HttpURLConnection来处理这些细节,当然也是可能实现的,只是处理起来难度就大了。

1.2.2 关于HttpClient

为了更好地处理向Web站点请求,包括处理Session、Cookie等细节问题,Apache开源组织提供了一个HttpClient项目,看它的名称就知道,它是一个简单的HTTP客户端(并不是浏览器),可以用于发送HTTP请求,接收HTTP响应。但不会缓存服务器的响应,不能执行HTML页面中嵌入的Javascript代码;也不会对页面内容进行任何解析、处理。
简单来说,HttpClient就是一个增强版的HttpURLConnection,HttpURLConnection可以做的事情HttpClient全部可以做;HttpURLConnection没有提供的有些功能,HttpClient也提供了,但它只是关注于如何发送请求、接收响应,以及管理HTTP连接。
使用HttpClient发送请求、接收响应很简单,只要如下几步即可。

创建HttpClient对象。
如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。
如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。
调用HttpClient对象的execute(HttpUriRequest request)发送请求,执行该方法返回一个HttpResponse。
调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
另外,Android已经成功地集成了HttpClient,这意味着开发人员可以直接在Android应用中使用Httpclient来访问提交请求、接收响应。
比如一个Android应用需要向指定页面发送请求,但该页面并不是一个简单的页面,只有当用户已经登录,而且登录用户的用户名有效时才可访问该页面。如果使用HttpURLConnection来访问这个被保护的页面,那么需要处理的细节就太复杂了。
其实访问Web应用中被保护的页面,使用浏览器则十分简单,用户通过系统提供的登录页面登录系统,浏览器会负责维护与服务器之间的Sesion,如果用户登录的用户名、密码符合要求,就可以访问被保护资源了。
在Android应用程序中,则可使用HttpClient来登录系统,只要应用程序使用同一个HttpClient发送请求,HttpClient会自动维护与服务器之间的Session状态,也就是说程序第一次使用HttpClient登录系统后,接下来使用HttpClient即可访问被保护页了。

二.在代码中的实际使用

**
2.1 HttpClient使用

使用背景:Android 客户端访问Java后台,获取后台数据,数据请求和下载都采用UTF-8编码。

/**
     * 请求服务器的方法
     * @param Url
     * @param jso
     * @return
     */
    public String NetWork(String servletName,List<String> Name,List<String> Value){
        //创建默认的 HttpClient 实例
        HttpClient httpClient = new DefaultHttpClient();
        // 采用http "Post"访问Java后台
        HttpPost httpPost = new HttpPost(url+servletName);  
        //post方式需要传入的参数
        HttpParams hp = httpPost.getParams();
        hp.setIntParameter(HttpConnectionParams.SO_TIMEOUT, 30000); // 超时设置
        hp.setIntParameter(HttpConnectionParams.CONNECTION_TIMEOUT, 30000);// 连接超时
        try {

            if(Name!=null){
                // 来自org.apache.http包。用于保存键值对name-value
                List<NameValuePair> formParams = new ArrayList<NameValuePair>();
                for (int i = 0; i < Name.size(); i++) {
                    formParams.add(new BasicNameValuePair(Name.get(i), Value.get(i)));
                }
                //网络访问传入的参数统一采用UTF-8编码
                UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(formParams, "UTF-8");
                httpPost.setEntity(urlEncodedFormEntity);
            }
            // java后台返回的response对象
            HttpResponse httpResponse = null;
            httpResponse = httpClient.execute(httpPost);
            //respons对象携带的内容
            HttpEntity httpEntity = httpResponse.getEntity();
            if (httpEntity != null) 
            {
                //java 后台返回内容采用UTF-8编码
                String content = handleEntity(httpEntity, "UTF-8");
                return content;
            }
            else
            {
                return null;
            }
        } catch (Exception e) 
        {
            e.printStackTrace();



            return null;
        }finally {
            //关闭连接,释放资源
            httpClient.getConnectionManager().shutdown();
        }
    }

2.1.1 关于handleEntity
网络数据的处理说到底也是对java字节流的处理,handleEntity就是对java后台返回的字节流进行处理后封装为字符对象。
代码如下:

public String handleEntity(HttpEntity entity, String charset)
    throws IOException {
        if (entity == null)
            return null;
        //网络数据使用字节流对象存储
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        //字节buffer
        byte[] buffer = new byte[1024];
        @SuppressWarnings("unused")

        int len = -1;
        //获取输入字节流对象
        InputStream is = entity.getContent();
        while ((len = is.read(buffer)) != -1) 
        {
            //将buffer数据写入字节流对象outStream
            outStream.write(buffer, 0, len);

        }

        byte[] data = outStream.toByteArray();
        outStream.close();
        is.close();
        // 生成指定编码格式的字符
        return new String(data, charset);
    }

2.2 HttpURLConnection 使用

2.2.1 获取输入流

// //由于得到一个InputStream对象  
    public InputStream getInputStream(String urlStr) throws IOException
    {
        InputStream is = null;
        try
        {
            url = new URL(urlStr);
            HttpURLConnection urlConn = (HttpURLConnection)url.openConnection();

            urlConn.setRequestMethod("GET");
            urlConn.setDoInput(true);
            urlConn.setDoOutput(true);
            urlConn.setUseCaches(false);
            urlConn.setConnectTimeout(5000);
            urlConn.setReadTimeout(5000);


            ////实现连接
            urlConn.connect();
            if (urlConn.getResponseCode() == 200) 
            {

                is  = urlConn.getInputStream()  ;
            //}

        }
        catch(MalformedURLException e)
        {
            e.printStackTrace();
        }
        return is;
    }

2.2.2 获取InputStream后转化为Bitmap

图片操作
//网络图片获取
    //urlpath = "http://*****/splashy_above.png"
    public static Bitmap getImage(String urlpath)
    throws Exception
    {

        URL url = new URL(urlpath);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(5*1000);
        conn.setRequestMethod("GET");
        Bitmap bitmap = null;


        if(conn.getResponseCode() == 200)
        {
            InputStream inputStream = conn.getInputStream();
            bitmap = BitmapFactory.decodeStream(inputStream);
        }

        return bitmap;
    }

2.2.3 将 Input对象写入文件

文件操作。
*将一个InputStream里面的数据写入到SD卡中
     * */
    public File write2SDFromInput(String path,String fileName,InputStream input)
    {
        File file = null;
        OutputStream output = null;
        try
        {

             //将一个InputStream 对象的数据写入到  BufferedOutputStream bos对象中
             byte[] arr = new byte[1];

            //缓冲流和字节流对象对接
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             BufferedOutputStream bos = new BufferedOutputStream(baos);


              //循环操作将输入流inputStream对象中的字节流写入到字节缓存输出流中
              int n = input.read(arr);
              while (n > 0) 
              {
                bos.write(arr);
                n = input.read(arr);
              }
              bos.close();


              //该函数只是在SD卡上创建文件夹
              createSDDir(path);
              //在上面创建好文件夹下创建文件
              file = createSDFile(path + fileName);

              output = new FileOutputStream(file);
              output.write(baos.toByteArray());

              output.flush();
              baos.close();

        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            try
            {

                output.close();
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
        return file;
    }

**

三.总结


网上查阅一番资料后再对照下自己的代码,恍然间好困!看代码真的好累啊…呵呵,其实网络操作说白了就是java的网络核心篇加上java的字节流操作,其中需要注意的地方先列出来以备后用:

1.HTTP协议之post和get的区别。
2.在Android 4.0之后,不允许在主线程中进行网络连接,必须采用线程机制。
3.建议采用线程 –Hander –Message 的Android消息机制进行网络数据操作。

Thread t.start();
        t.run()里面进行网络访问。
        t.run()之后访问完成后发送Message。

        Handler 截获message后将接收到的数据在View中予以展示。