Android Http 網絡探索

一、Http 請求及相應

1.1 請求包結構

Http 請求包結構

示例:

POST /meme.php/home/user/login HTTP/1.1
Host: 114.215.86.90
Cache-Control: no-cache
Postman-Token: bd243d6b-da03-902f-0a2c-8e9377f6f6ed
Content-Type: application/x-www-form-urlencoded

tel=13637829200&password=123456

1.2 響應包結構

Http 響應包結構

示例:

 HTTP/1.1 200 OK
    Date: Sat, 02 Jan 2016 13:20:55 GMT
    Server: Apache/2.4.6 (CentOS) PHP/5.6.14
    X-Powered-By: PHP/5.6.14
    Content-Length: 78
    Keep-Alive: timeout=5, max=100
    Connection: Keep-Alive
    Content-Type: application/json; charset=utf-8

    {"status":202,"info":"\u6b64\u7528\u6237\u4e0d\u5b58\u5728\uff01","data":null}

二、Http 請求方式

2.1 GET

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

2.2 POST

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

2.3 HEAD

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

2.4 PUT

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

2.5 DELETE

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

2.6 CONNECT

HTTP/1.1協議中預留給能夠將連接改爲管道方式的代理服務器。

2.7 OPTIONS

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

2.8 TRACE

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

其中,常用請求方式 GETPOST ,接下里就一起來熟悉下這兩種常用請求方式。

三、GET & POST

  • GET 通常是從服務器獲取數據,POST 通常是香服務器傳輸數據。
  • GET 是把參數數據隊列加到表單的 ACTION 屬性所指的 URL 中,值和表單內各個字段一一對應,在 URL 中可以看到,實際上就是 URL 拼接方式。POST 是通過 HTTP POST 機制,將表單內各個字段與其內容放置在 HTML HEADER 中一起傳送到 ACTION 屬性所指向的 URL 中。
  • 服務器通過 Request.QueryString 獲取 GET 變量值,通過 Request.Form 獲取 POST 提交的值。
  • GET 提交的數據量小,不能大於 1kb。POST 提交的數據量較大,一般被默認爲不受限制,但理論上,IIS4 中最大量爲 80KB,IIS5 中爲 100KB。
  • GET 安全性低,POST 安全性高。

四、使用 HttpURLConnection 發送 HTTP 請求

以前,Android 上發送 HTTP 請求有兩種方式,HttpURLConnection 和 HttpClient。不過由於 HttpClient 存在 API 數量衆多,維護困難等缺點,Android 大版本6.0之後,HttpClient 就被完全移除了。現在官方建議使用 HttpURLConnection。

4.1 流程

4.1.1 獲取 HttpURLConnection 實例

new 一個 URL 對象,並傳入要訪問的地址,然後調用一下 HttpURLConnection 的 openConnection() 方法即可。

URL lURL = new URL("https://www.baidu.com/");//請求 url
lHttpURLConnection = (HttpURLConnection) lURL.openConnection();

4.1.2 設置 HTTP 請求方法

lHttpURLConnection.setRequestMethod("GET");//請求方法,           HttpURLConnection 默認請求方式即爲 GET 請求,所以本句可省略

4.1.3 接下來可自由定製

如連接超時、讀取超時的設置,或服務器希望得到的一些消息頭等

lHttpURLConnection.setConnectTimeout(8000);//與服務器建立連接超時設置
lHttpURLConnection.setReadTimeout(8000);//與服務器傳遞數據的超時設置

4.1.4 讀取返回的輸入流

然後調用 getInstreamInput() 方法獲取服務器返回的輸入流
對返回的輸入流進行讀取

InputStream lInputStream = lHttpURLConnection.getInputStream();

4.1.5 關閉 HTTP 連接

lHttpURLConnection.disconnect();

4.2 示例

4.2.1 佈局

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.ginkwang.networktest">

    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

4.2.2 代碼

public class MainActivity extends AppCompatActivity {

    private Button mBtnSendRequest;
    private TextView mTvResponseTxt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        mBtnSendRequest = (Button) findViewById(R.id.btn_send_request);
        mBtnSendRequest.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sendRequestWithHttpURLConnection();
            }
        });

        mTvResponseTxt = (TextView) findViewById(R.id.tv_response_txt);
    }

    private void sendRequestWithHttpURLConnection() {
        //開啓線程發起請求
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection lHttpURLConnection = null;
                BufferedReader lBufferedReader = null;
                try {
                    URL lURL = new URL("https://www.baidu.com/");//請求 url
                    lHttpURLConnection = (HttpURLConnection) lURL.openConnection();
                    lHttpURLConnection.setRequestMethod("GET");//請求方法,HttpURLConnection 默認請求方式即爲 GET 請求,所以本句可省略
                    lHttpURLConnection.setConnectTimeout(8000);//與服務器建立連接超時設置
                    lHttpURLConnection.setReadTimeout(8000);//與服務器傳遞數據的超時設置

                    InputStream lInputStream = lHttpURLConnection.getInputStream();
                    //讀取獲取到的輸入流
                    lBufferedReader = new BufferedReader(new InputStreamReader(lInputStream));
                    StringBuilder lStringBuilder = new StringBuilder();
                    String line = "";
                    while ((line = lBufferedReader.readLine()) != null) {
                        lStringBuilder.append(line);
                    }
                    showResponse(lStringBuilder.toString());
                } catch (Exception pE) {
                    pE.printStackTrace();
                } finally {
                    if (lBufferedReader != null) {
                        try {
                            lBufferedReader.close();
                        } catch (IOException pE) {
                            pE.printStackTrace();
                        }
                    }

                    if (lHttpURLConnection != null) {
                        lHttpURLConnection.disconnect();
                    }
                }
            }
        }).start();
    }

    private void showResponse(final String result) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // 線程切回到主線程,進行 UI 更新操作
                mTvResponseTxt.setText(result);
            }
        });
    }
}

4.2.3 權限聲明

<uses-permission android:name="android.permission.INTERNET"/>

4.2.4 效果

Http 請求

4.3 HttpURLConnection 參數詳解

4.3.1 setRequestMethod

設置請求方法

4.3.2 setDoOutPut

設置是否向 HttpURLConnection 輸出,Get 請求用不到 lHttpURLConnection.getOutputStream(),因爲參數直接追加在地址後面,因此默認是false。而 Post 請求參數要放在 http 正文內,因此需要設爲 true。

4.3.3 setDoInPut

設置是否從 HttpUrlConnection 讀入,默認情況下是 true;指示應用程序要從 URL 連接讀取數據。

4.3.4 setConnectTimeout

與服務器建立連接超時設置

4.3.5 setReadTimeOut

與服務器傳遞數據的超時設置

4.3.6 setInstanceFollowRedirects

設置本次連接是否自動重定向

4.3.7 setRequestProperty

設置頭信息,如格式等,一般不設置,有默認設置

4.3.8 connect

不必要調用 connect 方法(調用也無妨),實際上只是建立了一個與服務器的 tcp 連接,並沒有實際發送 http 請求。無論是 post 還是 get,http請求實際上直到HttpURLConnection 的 getInputStream() 這個函數裏面才正式發送出去。

4.4 基於 HttpURLConnection 的 HTTP 請求封裝

直接上代碼

/**
 * 網絡請求類
 * Created by Wang-gk on 2017/5/2.
 */
public class NetworkHandler {

    private static final String TAG = "NetworkHandler";
    private static final int TIMEOUT = 15000;//超時設置

    /**
     * 請求參數格式轉換
     * @param pMap 請求參數
     * @return
     */
    private static String prepareParam(Map<String, Object> pMap) {
        StringBuffer lStringBuffer = new StringBuffer();
        if (pMap.isEmpty()) {
            return "";
        } else {
            for (String key : pMap.keySet()) {
                String value = (String) pMap.get(key);
                if (lStringBuffer.length() < 1) {
                    lStringBuffer.append(key).append("=").append(value);
                } else {
                    lStringBuffer.append("&").append(key).append("=").append(value);
                }
            }
        }
        return lStringBuffer.toString();
    }

    /**
     * Get 請求
     * @param pUrl 請求地址
     * @param pParamMap 請求參數
     * @param pListener 接口回調
     */
    public static void doGet(String pUrl, Map<String, Object> pParamMap, NetworkCallbackListener pListener) {
        HttpURLConnection lHttpURLConnection = null;
        BufferedReader lBufferedReader = null;
        if (pParamMap != null && pParamMap.size() > 0) {
            pUrl += prepareParam(pParamMap);
        }
        try {
            URL lURL = new URL(pUrl);
            lHttpURLConnection = (HttpURLConnection) lURL.openConnection();//初始化 HttpURLConnection
            lHttpURLConnection.setRequestMethod("GET");//設置請求方法
            lHttpURLConnection.setDoOutput(false);//設置是否向connection輸出, get請求用不到 lHttpURLConnection.getOutputStream(),因爲參數直接追加在地址後面,因此默認是false
            lHttpURLConnection.setDoInput(true);// 設置是否從 HttpUrlConnection 讀入,默認情況下是 true;指示應用程序要從 URL 連接讀取數據。
            lHttpURLConnection.setConnectTimeout(TIMEOUT);//與服務器建立連接超時設置
            lHttpURLConnection.setReadTimeout(TIMEOUT);//與服務器傳遞數據的超時設置
            lHttpURLConnection.setInstanceFollowRedirects(true);//設置本次連接是否自動重定向
            lHttpURLConnection.setRequestProperty("Content-Type","text/html; charset=UTF-8");//設置頭信息,如格式等,一般不設置,有默認設置
            lHttpURLConnection.connect();//不必要調用connect方法(調用也無妨),實際上只是建立了一個與服務器的tcp連接,並沒有實際發送http請求。無論是post還是get,http請求實際上直到HttpURLConnection的getInputStream()這個函數裏面才正式發送出去。
            InputStream lInputStream = lHttpURLConnection.getInputStream();
            //讀取獲取到的輸入流
            lBufferedReader = new BufferedReader(new InputStreamReader(lInputStream));
            StringBuilder lStringBuilder = new StringBuilder();
            String line = "";
            while ((line = lBufferedReader.readLine()) != null) {
                lStringBuilder.append(line);
            }
            Log.i(TAG, "doGet: output <<< " + lStringBuilder.toString());
            if (pListener != null) {
                pListener.onFinish(lStringBuilder.toString());
            }
        } catch (Exception pE) {
            if (pListener != null) {
                pListener.onError(pE);
            }
        } finally {
            if (lHttpURLConnection != null) {
                lHttpURLConnection.disconnect();
            }

            if (lBufferedReader != null) {
                try {
                    lBufferedReader.close();
                } catch (IOException pE) {
                    pE.printStackTrace();
                }
            }
        }
    }

    /**
     * Post 請求
     * @param pUrl 請求地址
     * @param pParamMap 請求參數
     * @param pListener 接口回調
     */
    public static void doPost(String pUrl, Map<String, Object> pParamMap, NetworkCallbackListener pListener) {
        HttpURLConnection lHttpURLConnection = null;
        BufferedReader lBufferedReader = null;
        try {
            URL lURL = new URL(pUrl);
            lHttpURLConnection = (HttpURLConnection) lURL.openConnection();//初始化 HttpURLConnection
            lHttpURLConnection.setRequestMethod("POST");//設置請求方法
            lHttpURLConnection.setDoOutput(true);//設置是否向connection輸出,因爲這個是 post 請求,參數要放在 http 正文內,因此需要設爲 true
            lHttpURLConnection.setDoInput(true);// 設置是否從 HttpUrlConnection 讀入,默認情況下是 true;指示應用程序要從 URL 連接讀取數據。
            lHttpURLConnection.setConnectTimeout(TIMEOUT);//與服務器建立連接超時設置
            lHttpURLConnection.setReadTimeout(TIMEOUT);//與服務器傳遞數據的超時設置
            lHttpURLConnection.setInstanceFollowRedirects(true);//設置本次連接是否自動重定向
            lHttpURLConnection.setRequestProperty("Content-Type","text/html; charset=UTF-8");//設置頭信息,如格式等,一般不設置,有默認設置
            lHttpURLConnection.connect();//不必要調用connect方法(調用也無妨),實際上只是建立了一個與服務器的tcp連接,並沒有實際發送http請求。無論是post還是get,http請求實際上直到HttpURLConnection的getInputStream()這個函數裏面才正式發送出去。
            InputStream lInputStream = lHttpURLConnection.getInputStream();
            //讀取獲取到的輸入流
            lBufferedReader = new BufferedReader(new InputStreamReader(lInputStream));
            StringBuilder lStringBuilder = new StringBuilder();
            String line = "";
            while ((line = lBufferedReader.readLine()) != null) {
                lStringBuilder.append(line);
            }
            Log.i(TAG, "doPost: output <<< " + lStringBuilder.toString());
            if (pListener != null) {
                pListener.onFinish(lStringBuilder.toString());
            }
        } catch (Exception pE) {
            if (pListener != null) {
                pListener.onError(pE);
            }
        } finally {
            if (lHttpURLConnection != null) {
                lHttpURLConnection.disconnect();
            }

            if (lBufferedReader != null) {
                try {
                    lBufferedReader.close();
                } catch (IOException pE) {
                    pE.printStackTrace();
                }
            }
        }
    }
}

其中,網絡請求的回調操作也封裝在內。主要就是交易完成 onFinish() 和 onError() 兩個回調方法,在接口回調類 NetworkCallbackListener 中定義

/**
 * 網絡處理接口
 * Created by Wang-gk on 2017/5/4.
 */
public interface NetworkCallbackListener {
    void onFinish(String pResponse);
    void onError(Exception pE);
}

五、參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章