Android 網絡(一)

1、Http協議

一、HTTP簡介

    大多數的Android應用程序都會使用HTTP協議來發送和接收網絡數據,Android中主要提供了兩種方式來進行HTTP操做,HttpURLConnection和HttpClient。因此首先介紹下Http協議。HTTP(HyperText Transfer Protocol,超文本傳輸協議) 是互聯網上應用最爲普遍的一種網絡協議。全部的WWW文件都必須遵照這個標準。HTTP協議採用了請求/響應模型。一般,由HTTP客戶端發起一個請求,創建一個到服務器指定端口(默認是80端口)的TCP鏈接,HTTP服務器則在那個端口監聽客戶端發送過來的請求。一旦收到請求,服務器(向客戶端)發回一個狀態行和響應消息,最後消息傳送完畢時,能夠關閉客戶端和服務器端的鏈接。java

二、HTTP消息

    Http消息由從客戶機到服務器的請求和從服務器到客戶機的響應構成。這兩種類型的消息均由一個起始行,一個或者多個頭域,一個指示頭域結束的空行和可選的消息體組成。HTTP的頭域包括通用頭,請求頭,響應頭和實體頭四個部分。android

    請求消息格式以下:緩存

    請求行 - 通用信息頭 - 請求頭 - 實體頭 - 消息主體服務器

     以下所示:網絡

    

    應答消息格式以下:異步

    狀態行 - 通用信息頭 - 響應頭 - 實體頭 - 消息主體ide

     以下所示:工具

    

 三、請求方法

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

方法性能

 

GET

請求指定url的數據,請求體爲空(例如打開網頁)

POST

請求指定url的數據,同時傳遞參數(在請求體中)

HEAD

相似於get請求,只不過返回的響應體爲空,用於獲取響應頭

PUT

從客戶端向服務器傳送的數據取代指定的文檔的內容

DELETE

請求服務器刪除指定的頁面

CONNECT

HTTP/1.1協議中預留給可以將鏈接改成管道方式的代理服務器

OPTIONS

容許客戶端查看服務器的性能

TRACE

回顯服務器收到的請求,主要用於測試或診斷。

 四、狀態碼詳解(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

請求未完成。服務器不支持所請求的功能

五、頭域詳解

  • 通用頭域

    通用頭域包含請求和響應消息都支持的頭域,通用頭域包含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-MD五、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實體頭指定服務器上保存內容的最後修訂時間 

2、HttpClient & HttpURLConnection

一、HttpClient VS HttpURLConnection

    HttpClient和HttpURLConnection是Android中提供的兩種進行HTTP操做的方式,這兩種方式都支持HTTPS協議、以流的形式進行上傳和下載、配置超時時間、IPv六、以及鏈接池等功能。

      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團隊也在積極對該方式進行維護和升級。

二、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;
    }

}

 

3、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);
相關文章
相關標籤/搜索