Android 网络(一)

China☆狼群 提交于 2019-12-04 05:58:44

一、Http协议

1、HTTP简介

    大多数的Android应用程序都会使用HTTP协议来发送和接收网络数据,Android中主要提供了两种方式来进行HTTP操作,HttpURLConnection和HttpClient。所以首先介绍下Http协议。HTTP(HyperText Transfer Protocol,超文本传输协议) 是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。HTTP协议采用了请求/响应模型。通常,由HTTP客户端发起一个请求,建立一个到服务器指定端口(默认是80端口)的TCP连接,HTTP服务器则在那个端口监听客户端发送过来的请求。一旦收到请求,服务器(向客户端)发回一个状态行和响应消息,最后消息传送完毕时,可以关闭客户端和服务器端的连接。

2、HTTP消息

    Http消息由从客户机到服务器的请求和从服务器到客户机的响应构成。这两种类型的消息均由一个起始行,一个或者多个头域,一个指示头域结束的空行和可选的消息体组成。HTTP的头域包括通用头,请求头,响应头和实体头四个部分。

    请求消息格式如下:

    请求行 - 通用信息头 - 请求头 - 实体头 - 消息主体

     如下所示:

    

    应答消息格式如下:

    状态行 - 通用信息头 - 响应头 - 实体头 - 消息主体

     如下所示:

    

 3、请求方法

    注:Android HttpUrlConnection中只未提供Connect方法        

方法

 

GET

请求指定url的数据,请求体为空(例如打开网页)

POST

请求指定url的数据,同时传递参数(在请求体中)

HEAD

类似于get请求,只不过返回的响应体为空,用于获取响应头

PUT

从客户端向服务器传送的数据取代指定的文档的内容

DELETE

请求服务器删除指定的页面

CONNECT

HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器

OPTIONS

允许客户端查看服务器的性能

TRACE

回显服务器收到的请求,主要用于测试或诊断。

 4、状态码详解(Status Code)

    状态码是一个用于标识服务器响应结果的代码,由3位数字表示。状态码的第一个数字定义响应的类别,后两个数字没有分类的作用。(状态码后面会跟随一段用于简要描述状态码信息的文本)

    状态码类型:

类型

类型描述

1xx

信息响应类,表示接收到请求并且继续处理

2xx

处理成功响应类,表示动作被成功接收、理解和接受

3xx

重定向响应类,为了完成指定的动作,必须接受进一步处理

4xx

客户端错误,客户请求包含语法错误或者是不能正确执行

5xx

服务端错误,服务器不能正确执行一个正确的请求

    常见状态码详解:

状态码

描述

200 OK

请求成功(其后是对GET和POST请求的应答文档)

301 Moved Permanently

所请求的页面已经转移至新的url

304 Not Modified

服务器端的数据在If-modified-since后未发生修改

400 Bad Request

服务器未能理解请求

401 Unauthorized

被请求的页面需要用户名和密码

404 Not Found

服务器无法找到被请求的页面

408 Request Timeout

请求超出了服务器的等待时间

500 Internal Server Error

请求未完成。服务器遇到不可预知的情况

501 Not Implemented

请求未完成。服务器不支持所请求的功能

5、头域详解

  • 通用头域

    通用头域包含请求和响应消息都支持的头域,通用头域包含Cache-Control、Connection、Date、Pragma、Transfer-Encoding、Upgrade、Via.
    1) Cache-Control:Cache-Control指定请求和响应遵循的缓存机制。请求时的缓存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached,响应消息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age。
    2) Date头域:Date头域表示消息发送的时间,如Date:Mon,31Dec200104:25:57GMT

  • 请求头域

    请求头域中包含请求消息特有的头域信息,如Host、Range、User-Agent等

    1) Host:Host头域指定请求资源的Intenet主机和端口号;

    2)Range:Range头域可以请求实体的一个或者多个子范围,如bytes=500-600,601-9993);

    3)User-Agent:User-Agent头域的内容包含发出请求的用户信息

  • 响应头域

    响应头域中包含响应消息中特有的头域信息,如Location、Server等

  • 1)Location:Location头域用于重定向接收者到一个新URI地址;

    2)Server:Server头域包含处理请求的原始服务器的软件信息;

  • 实体头域

    请求消息和响应消息都可以包含实体信息,实体信息一般由实体头域和实体组成。实体头域包含关于实体的原信息,实体头包括Allow、Content-Base、Content-Encoding、Content-Language、Content-Length、Content-Location、Content-MD5、Content-Range、Content-Type、Etag、Expires、Last-Modified、extension-header;

  • 1)Content-Type:Content-Type实体头用于向接收方指示实体的介质类型,指定HEAD方法送到接收方的实体介质类型,或GET方法发送的请求介质类型;

    2)Content-Range:Content-Range实体头用于指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。主要用于对范围请求的响应或对一系列范围的重叠请求。
    一般格式:Content-Range:bytes-unitSPfirst-byte-pos-last-byte-pos/entity-legth
    如:Content-Range:bytes0-499/1234;

    3)Last-modified:Last-modified实体头指定服务器上保存内容的最后修订时间 

二、HttpClient & HttpURLConnection

1、HttpClient VS HttpURLConnection

    HttpClient和HttpURLConnection是Android中提供的两种进行HTTP操作的方式,这两种方式都支持HTTPS协议、以流的形式进行上传和下载、配置超时时间、IPv6、以及连接池等功能。

      DefaultHttpClient和AndroidHttpClient都是HttpClient的具体实现类,都拥有众多的API,而且实现比较稳定,bug数量也很少。但同时也由于HttpClient的API数量过多,使得很难在不破坏兼容性的情况下对它进行升级和扩展,所以目前Android团队在提升和优化HttpClient方面的工作态度并不积极;

    而HttpURLConnection是一种多用途、轻量极的HTTP客户端,使用它来进行HTTP操作可以适用于大多数的应用程序,同时其升级与维护也相对比较简单。但在Android 2.3版本之前,HttpURLConnection一直存在着一些令人厌烦的bug。比如说对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效。通常的解决办法就是直接禁用掉连接池的功能。

     注意:Google建议Android2.3及以上版本的应用使用HttpURLConnection,而Android2.3以下版本的应用使用HttpClient。原因如下:

    在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。
    而在Android 2.3版本及以后,HttpURLConnection则是最佳的选择。它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。同时,Google Android团队也在积极对该方式进行维护和升级。

2、HttpURLConnection的使用

/**
 * 采用HttpURLConnection进行网络请求的工具类
 */
public class AsyncNetworkUtil {
    private static String TAG = "AsyncNetworkUtil";

    /**
     * 发送一个post请求
     * @param url:请求的地址
     * @param content:post请求参数
     * @return: 返回服务器响应信息
     */
    public static String post(String url, String content) {
        HttpURLConnection conn = null;
        String response = null;
        try {
            // 创建一个URL对象
            URL mURL = new URL(url);
            // 调用URL的openConnection()方法,获取HttpURLConnection对象
            conn = (HttpURLConnection) mURL.openConnection();

            conn.setRequestMethod("POST");// 设置请求方法为post
            conn.setReadTimeout(5000);// 设置读取超时为5秒
            conn.setConnectTimeout(10000);// 设置连接网络超时为10秒
            conn.setUseCaches(false);   //设置不使用缓存
            conn.setDoOutput(true);// 设置此方法,允许向服务器输出内容

            // post请求的参数
            String data = content;
            // 获得一个输出流,向服务器写数据,默认情况下,系统不允许向服务器输出内容
            OutputStream out = conn.getOutputStream();
            out.write(data.getBytes());
            out.flush();
            out.close();

            int responseCode = conn.getResponseCode();// 调用此方法就不必再使用conn.connect()方法
            if (responseCode != -1) {
                InputStream is = conn.getInputStream();
                response = getStringFromInputStream(is);
            } else {
                response = "No valid response code";
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();// 关闭连接
            }
        }
        return response;
    }

    /**
     * 发送一个get请求
     * @param url:请求的地址
     * @return: 响应信息
     */
    public static String get(String url) {
        HttpURLConnection conn = null;
        try {
            // 利用string url构建URL对象
            URL mURL = new URL(url);
            conn = (HttpURLConnection) mURL.openConnection();

            conn.setRequestMethod("GET");
            conn.setReadTimeout(5000);
            conn.setConnectTimeout(10000);
            conn.connect();

            int responseCode = conn.getResponseCode();
            if (responseCode == 200) {
                InputStream is = conn.getInputStream();
                String response = getStringFromInputStream(is);
                return response;
            }else if ((responseCode + "") != null && (responseCode + "").length() != 0){
                return responseCode + " ";
            }
            else {
                throw new NetworkErrorException("response status is "+responseCode);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            if (conn != null) {
                conn.disconnect();
            }
        }
        return null;
    }

    /**
     * 从输入流中获取数据
     * @param is
     * @return
     * @throws IOException
     */
    private static String getStringFromInputStream(InputStream is)
            throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        // 模板代码 必须熟练
        byte[] buffer = new byte[1024];
        int len = -1;
        while ((len = is.read(buffer)) != -1) {
            os.write(buffer, 0, len);
        }
        is.close();
        String state = os.toString();// 把流中的数据转换成字符串,采用的编码是utf-8(模拟器默认编码)
        os.close();
        return state;
    }

}

 

三、Android网络操作一般流程

  1. 开启网络权限

  2. <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  3. 检查设备的网络连接状况

  4.  /**
     * 这是一个工具类,用于检测网络连接状况
     */
    public class NetworkConnectivityUtil {
        private static String TAG = "NetworkConnectivityUtil";  //Log Tag
        private static ConnectivityManager connMgr; //网络连接管理器对象
        public NetworkConnectivityUtil(Context ctx){
            connMgr = (ConnectivityManager) ctx.getSystemService(ctx.CONNECTIVITY_SERVICE);
        }
        /**
         * 检查网络连接状况
         * @return  true:网络连通
         *          false:网络未连通
         */
        public boolean checkNetwork() {
            NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
            return networkInfo.isConnected() ? true : false;
        }
        /**
         * 检查WIFI连接状况
         * @return  true:网络连通
         *          false:网络未连通
         */
        public boolean checkWifi() {
            NetworkInfo wifiInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
            return wifiInfo.isConnected() ? true : false;
        }
        /**
         * 检查手机网络连接状况
         * @return  true:网络连通
         *          false:网络未连通
         */
        public boolean checkMobile() {
            NetworkInfo mobileInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
            return mobileInfo.isConnected() ? true : false;
        }
    }
  5. 在工作线程中进行网络操作

  6. /**
     * 通过一个异步任务AsyncTask进行网络操作
     */
     new AsyncTask<String, Void, String>() {
       @Override
       protected String doInBackground(String... params) {
          //NetworkUtil.get即上面HttpURLConnection中的方法
          return NetworkUtil.get(params[0]);
       }
       @Override
       protected void onPostExecute(String s) {
          editText.setText(s.substring(0, 100));
       }
    }.execute(url);
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!