Android進階(2)| 網路

本節目錄
使用WebView控制元件訪問網路
WebView這個控制元件主要是讓我們的程式能夠在不開啟手機內建瀏覽器的情況下訪問網頁,就相當於是在我們的程式中安放了一下小型的瀏覽器,從而讓我們的程式能夠連線到網際網路上,從而能夠很輕鬆的展示各種網頁。
在程式中使用WebView訪問網路主要有三個步驟:
第一步:在佈局中設定WebView控制元件,並在主程式碼中獲取到WebView例項。
第二步:使用getSettings()來對對WebView進行設定。
第二步:對WebView使用loadUrl()方法,並將網址傳入,之後就能夠訪問網頁了。
知道了主要步驟,我們就來實踐一下。建立一個空專案,然後對佈局進行修改:
<?xml version="1.0" encoding="utf-8"?> <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"> </WebView> </LinearLayout>
我們只是在佈局中加了WebView的控制元件,這個控制元件和其他的控制元件的屬性相似,我們讓它完全的展開在父佈局上面。接著是修改主程式碼:
package com.example.yzbkaka.webviewtest; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.webkit.WebView; import android.webkit.WebViewClient; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); WebView webView = (WebView)findViewById(R.id.web_view);//獲取例項 webView.getSettings().setJavaScriptEnabled(true);//是否支援JS指令碼 webView.setWebViewClient(new WebViewClient());//是否支援換頁 webView.loadUrl("http://www.bing.com");//載入網站 } }
在主程式碼中我們先是使用findViewById()的方法獲取到了WebView的例項,接著是使用了getSettings()方法來為WebView設定一些屬性,這裡我們為WebView簡單設定了支援JS指令碼的屬性。接著是使用setWebViewClient()方法讓我們在WebView中開啟新的網頁時仍然是能夠在WebView中瀏覽,而不是去開啟手機瀏覽器。最後就是通過loadUrl方法將要開啟的網站傳入。
使用手機網路需要我們實現去申請許可權:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.yzbkaka.webviewtest"> <uses-permission android:name="android.permission.INTERNET" <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" 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>
最後在聯網的手機上執行程式,即可訪問bing了。
使用HTTP協議訪問網路
1.HTTP工作原理
HTTP的工作原理簡單來說客戶端想服務端的伺服器傳送一條HTTP請求,然後服務端在收到請求之後會返回一些資料給客戶端,最後客戶端再對這些資料進行處理和解析即可。
2.傳送HTTP
在Android原生的庫中我們一般是採用HttpURLConnection類來進行傳送HTTP請求的操作。我們來新建一個空專案來體驗一下。首先是修改主佈局程式碼:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/send" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="SEND"/> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/response" android:layout_width="match_parent" android:layout_height="match_parent" /> </ScrollView> </LinearLayout>
我們設定了一個開始按鈕,當之後我們執行程式時點選按鈕就可以傳送HTTP請求,並且會將資料返回來顯示在下面的TextView中。
接著我們修改主程式碼:
package com.example.yzbkaka.networktest; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private Button sendRequest; private TextView responseText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); sendRequest = (Button)findViewById(R.id.send); responseText = (TextView)findViewById(R.id.response); sendRequest.setOnClickListener(this); } @Override public void onClick(View view) { if(view.getId() == R.id.send){ sendHTTPWithURLConnection(); } } public void sendHTTPWithURLConnection(){ new Thread(new Runnable() { @Override public void run() { HttpURLConnection connection = null; BufferedReader reader = null; try{ URL url = new URL("http://www.bing.com"); connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(8000); connection.setReadTimeout(8000); InputStream in = connection.getInputStream(); reader = new BufferedReader(new InputStreamReader(in)); StringBuilder response = new StringBuilder(); String line; while((line = reader.readLine()) != null){ response.append(line); } showResponse(response.toString()); }catch(Exception e){ e.printStackTrace(); }finally { if(reader != null){ try{ reader.close(); }catch(IOException e){ e.printStackTrace(); } } if(connection != null){ connection.disconnect(); } } } }).start(); } public void showResponse(final String response){ runOnUiThread(new Runnable() { @Override public void run() { responseText.setText(response); } }); } }
我們使用HttpURLConnection類傳送HTTP請求主要是寫在sendHTTPWithURL Connection()方法當中,我們可以將其中的思路分解成4步:
第一步:獲取HttpURLConnection的例項。我們是先新建一個URL例項並將我們想要請求的地址輸入進去,接著就是使用openConnection()方法。
第二步:對HttpURLConnection的例項進行一些屬性的設定。setRequestMethod()是設定從伺服器獲取還是傳送資料;setConnectTimeout()是設定連線超時的毫秒數;setReadTimeout()是設定讀取超時的毫秒數。
第三步:使用Java流來讀取資料。
第四步:使用disconnect()方法來將連線斷開。
最後我們申明網路許可權,然後執行程式就會看到一堆HTML程式碼,這些就是伺服器返回給我們的資料。
解析資料
在前面我們傳送HTTP請求的時候選擇的網站伺服器是bing,但是由於bing是一個供我們使用的搜尋網站,因此它返回來的資料就是bing的HTML程式碼。不過我們建立能夠訪問網路的程式都會連線到自己的伺服器,自已的伺服器一般是用來供我們上傳資料或者提取資料的,因此我們的伺服器中應該是隻存在資料的,而這些資料一般有 XML和JSON 兩種格式。我們為了提取到我們需要的資料,就需要對這樣的格式進行解析。
1.解析XML資料
XML的資料與HTML的風格類似,比較容易理解

xml格式資料
一般解析XML格式的資料有兩種方法,下面來使用這兩種方法將上面的XML格式的資料來進行解析。
Pull解析
首先我們需要用一個String型別的responseData來儲存上述資料,接著我們可以封裝一個parseXMLWithPull()方法來進行具體的操作:
public void parseXMLWithPull(String responseData){ try{ XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); XmlPullParser xmlPullParser = factory.newPullParser();//獲取XmlPullParser例項 xmlPullParser.setInput(new StringReader(responseData));//將XML格式資料傳入到XmlPullParser int evenType = xmlPullParser.getEventType();//得到當前的解析事件 String id = ""; String name = ""; String version = ""; while(evenType != XmlPullParser.END_DOCUMENT){//如果沒有解析完,就開始解析 String nodeName = xmlPullParser.getName();//得到當前的標籤 switch(evenType){ case XmlPullParser.START_TAG:{ if("id".equals(nodeName)){//如果等於<id> id = xmlPullParser.nextText(); } if("name".equals(nodeName)){//如果等於<name> name = xmlPullParser.nextText(); } if("version".equals(nodeName)){ version = xmlPullParser.nextText();//如果等於<version> } break; } case XmlPullParser.END_TAG:{ if("app".equals(nodeName)){ //完成一個<app>標籤之後的操作 } break; } default: break; } evenType = xmlPullParser.next();//獲取下一個解析事件 } }catch(Exception e){ e.printStackTrace(); } }
SAX解析
採用SAX解析相對於Pull解析來說步驟比較的多,不過這種方法是很容易理解的。首先我們先建立一個Myhandler類並讓它繼承自DefaultHandler類,然後重寫父類的5個方法:
public class MyHandler extends DefaultHandler { private String nodeName; private StringBuilder id; private StringBuilder name; private StringBuilder version; @Override //在開始解析XML的時候呼叫 public void startDocument() throws SAXException { id = new StringBuilder(); name = new StringBuilder(); version = new StringBuilder(); } @Override //在開始解析標籤的時候呼叫 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { nodeName = localName;// 記錄當前結點名 } @Override //在獲取標籤內容的時候呼叫 public void characters(char[] ch, int start, int length) throws SAXException { if ("id".equals(nodeName)) {// 根據當前的結點名判斷將內容新增到哪一個StringBuilder物件中 id.append(ch, start, length); } else if ("name".equals(nodeName)) { name.append(ch, start, length); } else if ("version".equals(nodeName)) { version.append(ch, start, length); } } @Override //在對標籤解析完之後呼叫 public void endElement(String uri, String localName, String qName) throws SAXException { if ("app".equals(localName)) { //在解析完<app>標籤之後的操作 id.setLength(0);// 最後要將StringBuilder清空掉 name.setLength(0); version.setLength(0); } } @Override //在標籤解析完之後呼叫 public void endDocument() throws SAXException { super.endDocument(); } }
接著我們在主程式碼中來將XML傳入,我們還是將資料儲存到String型別的responseData中,然後封裝一個parseXMLWithSAX()方法來進行具體的操作:
public void parseXMLWithSAX(String responseData){ try{ SAXParserFactory factory = SAXParserFactory.newInstance(); XMLReader xmlReader = factory.newSAXParser().getXMLReader(); MyHandler handler = new MyHandler(); xmlReader.setContentHandler(handler);//將Myhandler載入進去 xmlReader.parse(new InputSource(new StringReader(responseData)));//將資料放進去進行解析 }catch (Exception e){ e.printStackTrace(); } }
2.解析JSON資料
JSON格式的資料相對於XML會更加的簡潔,同時解析JSON資料的方法也很簡單,可是使用Android官方提供的JSONObject,也可是通過新增GSON依賴來進行解析。

JSON格式資料
JSONObject解析
我們同樣,先使用String型別的responseData來儲存上面的的JSON資料,接著我們來封裝一個parseJSONWithJSONObject()的方法來進行具體的操作:
public void parseJSONWithJSONObject(String responseData){ try{ JSONArray jsonArray = new JSONArray(responseData);//將資料傳入到JSONArray例項當中 for(int i = 0;i < jsonArray.length();i++){ JSONObject jsonObject = jsonArray.getJSONObject(i);//按照次序將資料賦給JSONObject例項 String id = jsonObject.getString("id");//對JSONObject例項進行解析 String name = jsonObject.getString("name"); String version = jsonObject.getString("version"); Log.d("this","id is"+ id);//解析完得到資料後的操作 Log.d("this","name is"+ name); Log.d("this","version is"+ version); } }catch (Exception e){ e.printStackTrace(); } }
GSON解析
GSON是一個谷歌的開元庫,因此使用它我們需要先新增依賴:
implementation 'com.google.code.gson:gson:2.7'
GSON的特別之處在於它能夠JSON格式的資料自動對映為一個物件,而資料裡面的內容就是我們在這個物件裡面需要定義的資料。例如對上面的JSON資料來說,我們可以定義一個MyClass的類,並在該類裡面定義id、version和name,並寫出基本的Getter和Setter:
public class MyClass { String id; String name; String version; public void setId(String id) { this.id = id; } public void setName(String name) { this.name = name; } public void setVersion(String version) { this.version = version; } public String getId() { return id; } public String getName() { return name; } public String getVersion() { return version; } }
接著我們封裝一個parseJSONWithGSON()的方法來使用GSON解析資料:
public void parseJSONWithGSON(String responseData){ Gson gson = new Gson(); List<MyClass> myclassList = gson.fromJson(responseData,new TypeToken<MyClass>(){}.getType());//將資料傳入 for(MyClass myClass : myclassList){ Log.d("this","id is"+ myClass.getId());//解析完得到資料後的操作 Log.d("this","name is"+ myClass.getName()); Log.d("this","version is"+ myClass.getVersion()); } }