標籤:
Android基礎之十七使用網路技術
本章主要會講述如何在手機端使用HTTP協議和伺服器端進行網路互動,並對伺服器返回的資料進行解析,這也是Android中最常使用到的網路技術了,下面就讓我們一起來學習一下吧。
1 WebView的用法
藉助它我們就可以在自己的應用程式裡嵌入一個瀏覽器,從而非常輕鬆地展示各種各樣的網頁
WebView的用法也是相當簡單,下面我們就通過一個例子來學習一下吧。建立一個WebViewTest項目,然後修改activity_main.xml中的代碼,如下所示
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <WebView android:id="@+id/web_view" android:layout_width="match_parent" android:layout_height="match_parent" /></LinearLayout>
可以看到,我們在布局檔案中使用到了一個新的控制項,WebView。這個控制項當然也就是用來顯示網頁的了,這裡的寫法很簡單,給它設定了一個id,並讓它充滿整個螢幕。
然後修改MainActivity中的代碼,如下所示:
package com.example.webview;import android.os.Bundle;import android.app.Activity;import android.view.Menu;import android.webkit.WebView;import android.webkit.WebViewClient;public class MainActivity extends Activity {private WebView webview;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);webview=(WebView) findViewById(R.id.web_view);webview.getSettings().setJavaScriptEnabled(true);webview.setWebViewClient(new WebViewClient() {@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); // 根據傳入的參數再去載入新的網頁return true; // 表示當前WebView可以處理開啟新網頁的請求,不用藉助系統瀏覽器}});webview.loadUrl("http://www.baidu.com");}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}}
MainActivity中的代碼也很短,首先使用findViewById()方法擷取到了WebView的執行個體,然後調用WebView的getSettings()方法可以去設定一些瀏覽器的屬性,這裡我們並不去設定過多的屬性,只是調用了setJavaScriptEnabled()方法來讓WebView支援JavaScript指令碼。
接下來是非常重要的一個部分,我們調用了WebView的setWebViewClient()方法,並傳入了WebViewClient的匿名類作為參數,然後重寫了shouldOverrideUrlLoading()方法。這就表明當需要從一個網頁跳轉到另一個網頁時,我們希望目標網頁仍然在當前WebView中顯示,而不是開啟系統瀏覽器。
最後一步就非常簡單了,調用WebView的loadUrl()方法,並將網址傳入
另外還需要注意,由於本程式使用到了網路功能,而訪問網路是需要聲明許可權的,因此我們還得修改AndroidManifest.xml檔案,並加入許可權聲明,如下所示:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.webviewtest" android:versionCode="1" android:versionName="1.0" > …… <uses-permission android:name="android.permission.INTERNET" /> ……</manifest>
2 使用HTTP協議訪問網路
它的工作原理特別的簡單,就是用戶端向伺服器發出一條HTTP請求,伺服器收到請求之後會返回一些資料給用戶端,然後用戶端再對這些資料進行解析和處理就可以了。
2.1 使用HttpURLConnection
在Android上發送HTTP請求的方式一般有兩種,HttpURLConnection和HttpClient,本小節我們先來學習一下HttpURLConnection的用法。
首先需要擷取到HttpURLConnection的執行個體,一般只需new出一個URL對象,並傳入目標的網路地址,然後調用一下openConnection()方法即可,如下所示:
URL url = new URL("http://www.baidu.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
得到了HttpURLConnection的執行個體之後,我們可以設定一下HTTP請求所使用的方法。常用的方法主要有兩個,GET和POST。GET表示希望從伺服器那裡擷取資料,而POST則表示希望提交資料給伺服器。寫法如下:
connection.setRequestMethod("GET");
接下來就可以進行一些自由的定製了,比如設定連線逾時、讀取逾時的毫秒數,以及伺服器希望得到的一些訊息頭等。這部分內容根據自己的實際情況進行編寫,樣本寫法如下:
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
之後再調用getInputStream()方法就可以擷取到伺服器返回的輸入資料流了,剩下的任務就是對輸入資料流進行讀取,如下所示:
InputStream in = connection.getInputStream();
最後可以調用disconnect()方法將這個HTTP串連關閉掉,如下所示:
connection.disconnect();
下面就讓我們通過一個具體的例子來真正體驗一下HttpURLConnection的用法。建立一個NetworkTest項目,首先修改activity_main.xml中的代碼,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/send_request" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Send Request" /> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/response" android:layout_width="match_parent" android:layout_height="wrap_content" /> </ScrollView></LinearLayout>
注意這裡我們使用了一個新的控制項,ScrollView,它是用來做什麼的呢?由於手機螢幕的空間一般都比較小,有些時候過多的內容一屏是顯示不下的,藉助ScrollView控制項的話就可以允許我們以滾動的形式查看螢幕外的那部分內容。另外,布局中還放置了一個Button和一個TextView,Button用於發送HTTP請求,TextView用於將伺服器返回的資料顯示出來。
接著修改MainActivity中的代碼,如下所示:
public class MainActivity extends Activity implements OnClickListener {public static final int SHOW_RESPONSE = 0;private Button sendRequest;private TextView responseText;private Handler handler = new Handler() {public void handleMessage(Message msg) {switch (msg.what) {case SHOW_RESPONSE:String response = (String) msg.obj;// 在這裡進行UI操作,將結果顯示到介面上responseText.setText(response);}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);sendRequest = (Button) findViewById(R.id.send_request);responseText = (TextView) findViewById(R.id.response_text);sendRequest.setOnClickListener(this);}@Overridepublic void onClick(View v) {if (v.getId() == R.id.send_request) {sendRequestWithHttpURLConnection();}}private void sendRequestWithHttpURLConnection() {// 開啟線程來發起網路請求new Thread(new Runnable() {@Overridepublic void run() {HttpURLConnection connection = null;try {URL url = new URL("http://www.baidu.com");connection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(8000);connection.setReadTimeout(8000);InputStream in = connection.getInputStream();// 下面對擷取到的輸入資料流進行讀取BufferedReader reader = new BufferedReader(new InputStreamReader(in));StringBuilder response = new StringBuilder();String line;while ((line = reader.readLine()) != null) {response.append(line);}Message message = new Message();message.what = SHOW_RESPONSE;// 將伺服器返回的結果存放到Message中message.obj = response.toString();handler.sendMessage(message);} catch (Exception e) {e.printStackTrace();} finally {if (connection != null) {connection.disconnect();}}}}).start();}}
以看到,我們在Send Request按鈕的點擊事件裡調用了sendRequestWithHttpURL- Connection()方法,在這個方法中先是開啟了一個子線程,然後在子線程裡使用HttpURLConnection發出一條HTTP請求,請求的目標地址就是百度的首頁。接著利用BufferedReader對伺服器返回的流進行讀取,並將結果存放到了一個Message對象中。這裡為什麼要使用Message對象呢?當然是因為子線程中無法對UI進行操作了。我們希望可以將伺服器返回的內容顯示到介面上,所以就建立了一個Message對象,並使用Handler將它發送出去。之後又在Handler的handleMessage()方法中對這條Message進行處理,最終取出結果並設定到TextView上。
過在開始運行之前,仍然別忘了要聲明一下網路許可權。修改AndroidManifest.xml中的代碼
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.networktest" android:versionCode="1" android:versionName="1.0" > …… <uses-permission android:name="android.permission.INTERNET" /> ……</manifest>
好了,現在運行一下程式,並點擊Send Request按鈕,結果2所示。
這是返回的 htmL格式,另外如果也可以以json資料的格式返回。
那麼如果是想要提交資料給伺服器應該怎麼辦呢?其實也不複雜,只需要將HTTP請求的方法改成POST,並在擷取輸入資料流之前把要提交的資料寫出即可。注意每條資料都要以鍵值對的形式存在,資料與資料之間用&符號隔開,比如說我們想要向伺服器提交使用者名稱和密碼,就可以這樣寫:
connection.setRequestMethod("POST");
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes("username=admin&password=123456");
2.2 使用HttpClient
HttpClient是Apache提供的HTTP網路提供者,從一開始的時候就被引入到了Android API中。它可以完成和HttpURLConnection幾乎一模一樣的效果,但兩者之間的用法卻有較大的差別,那麼我們自然要看一下HttpClient是如何使用的了。
首先你需要知道,HttpClient是一個介面,因此無法建立它的執行個體,通常情況下都會建立一個DefaultHttpClient的執行個體,如下所示:
HttpClient httpClient = new DefaultHttpClient();
接下來如果想要發起一條GET請求,就可以建立一個HttpGet對象,並傳入目標的網路地址,然後調用HttpClient的execute()方法即可:
HttpGet httpGet = new HttpGet("http://www.baidu.com");
httpClient.execute(httpGet);
並傳入目標的網路地址,如下所示:
HttpPost httpPost = new HttpPost("http://www.baidu.com");
然後通過一個NameValuePair集合來存放待提交的參數,並將這個參數集合傳入到一個UrlEncodedFormEntity中,然後調用HttpPost的setEntity()方法將構建好的UrlEncodedFormEntity傳入,如下所示:
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("username", "admin"));
params.add(new BasicNameValuePair("password", "123456"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "utf-8");
httpPost.setEntity(entity);
接下來的操作就和HttpGet一樣了,調用HttpClient的execute()方法,並將HttpPost對象傳入即可:
httpClient.execute(httpPost);
執行execute()方法之後會返回一個HttpResponse對象,伺服器所返回的所有資訊就會包含在這裡面。通常情況下我們都會先取出伺服器返回的狀態代碼,如果等於200就說明請求和響應都成功了,如下所示:
if (httpResponse.getStatusLine().getStatusCode() == 200) {
// 請求和響應都成功了
}
接下來在這個if判斷的內部取出服務返回的具體內容,可以調用getEntity()方法擷取到一個HttpEntity執行個體,然後再用EntityUtils.toString()這個靜態方法將HttpEntity轉換成字串即可,如下所示:
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity);
注意如果伺服器返回的資料是帶有中文的,直接調用EntityUtils.toString()方法進行轉換會有亂碼的情況出現,這個時候只需要在轉換的時候將字元集指定成utf-8就可以了,如下所示:
String response = EntityUtils.toString(entity, "utf-8");
好了,基本的用法就是如此,接下來就讓我們把NetworkTest這個項目改用HttpClient的方式再實現一遍吧。
由於布局部分完全不用改動,所以現在直接修改MainActivity中的代碼,如下所示
public class MainActivity extends Activity implements OnClickListener {……@Overridepublic void onClick(View v) {if (v.getId() == R.id.send_request) {sendRequestWithHttpClient();}}private void sendRequestWithHttpClient() {new Thread(new Runnable() {@Overridepublic void run() {try {HttpClient httpClient = new DefaultHttpClient();HttpGet httpGet = new HttpGet("http://www.baidu.com");HttpResponse httpResponse = httpClient.execute(httpGet);if (httpResponse.getStatusLine().getStatusCode() == 200) {// 請求和響應都成功了HttpEntity entity = httpResponse.getEntity();String response = EntityUtils.toString(entity, "utf-8");Message message = new Message();message.what = SHOW_RESPONSE;// 將伺服器返回的結果存放到Message中message.obj = response.toString();handler.sendMessage(message);}} catch (Exception e) {e.printStackTrace();}}}).start();}……}
Android基礎之十七使用網路技術