第一行程式碼——第九章:看看精彩的世界——使用網路技術
目錄:
知識點:
9.1 WebView的用法
要求在應用程式裡展示一些網頁,不允許開啟系統瀏覽器,這時候就要藉助WebView控制元件了。
建立WebActivity,最簡單
package com.dak.administrator.firstcode.net; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.webkit.WebView; import android.webkit.WebViewClient; import com.dak.administrator.firstcode.R; 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 WebActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_web); WebView webView = findViewById(R.id.web_view); //支援JavaScript指令碼 webView.getSettings().setJavaScriptEnabled(true); webView.setWebViewClient(new WebViewClient()); webView.loadUrl("http://www.baidu.com"); } }
Xml頁面:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <WebView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/web_view" /> </LinearLayout>
當然不要忘記加上網路許可權。
9.2 使用HTTP協議訪問網路
HTTP 協議,其工作原理很簡單:客戶端向伺服器發出一條 HTTP 請求,伺服器收到請求後會返回一些資料給客戶端,然後客戶端再對這些資料進行解析和處理。
9.2.1 使用HttpURLConnection
這裡我們使用HttpURLConnection ,對於任何網路請求,我們應該在子執行緒中進行。
這裡可以new Thread 一下,我就不寫了。
private void HttpUrl(){
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) new URL("www.baidu.com").openConnection();
InputStream s=connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(s));
String str = "";
while ((str = reader.readLine()) != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
若是想要提交資料給伺服器只需把請求方法改為 POST,並在獲取輸入流之前把要提交的資料寫出即可。注意每條資料要以鍵值對的形式存在,資料與資料之間用 “&” 隔開,比如向伺服器提交使用者名稱和密碼可寫成:
connection.setRequestMethod("POST");
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes("username=admin&password=123456");
9.2.2 使用OkHttp
Okhttp 是由大名鼎鼎的Square公司開發的,這個公司在開源事業上面貢獻良多,除了Okhttp外,還開發了像Picasso、Retrofit等著名的開源專案。
Okhttp 專案主頁地址是:https://github.com/square/okhttp
在使用 OKHttp 前,需要在專案中新增 OKHttp 庫的依賴,如下:
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
接下來建立Activity
package com.dak.administrator.firstcode.net;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import com.dak.administrator.firstcode.R;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class OkHttpActivity extends AppCompatActivity {
private static final String TAG = "OkHttpActivity===";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ok_http);
getRequest();
}
private void getRequest() {
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://www.baidu.com")
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
}
private void postRequest() {
try {
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new FormBody.Builder()
.add("username", "admin")
.add("password", "123456")
.build();
Request request = new Request.Builder()
.url("www.baidu.com")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
}
}
相比 HttpURLConnection,OKHttp 簡單易用,若是發起一條 POST 請求,會比 GET 請求稍微複雜點,需要構建一個 RequestBody 物件來存放待提交的引數:
RequestBody requestBody = new FormBody.Builder()
.add("username","admin")
.add("password","123456")
.build();
然後在 Request.Builder 中呼叫一下 post() 方法,並將 RequestBody 物件傳入:
Request request = new Request.Builder()
.url("http://www.baidu.com")
.post(RequestBody)
.build();
9.3 解析XML格式資料
在網路傳輸資料時,最常用的格式有兩種:XMl和JSON,下面我們就來一個個地學習吧。
首先建立一個xml檔案
<apps>
<app>
<id>1</id>
<name>Google maps</name>
<version>1.0</version>
</app>
<app>
<id>2</id>
<name>Chrome</name>
<version>2.1</version>
</app>
<app>
<id>3</id>
<name>Google Play</name>
<version>2.3</version>
</app>
</apps>
9.3.1 Pull解析方式
private void xmlWithPull(String xmlData) {
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlData));
int eventType = xmlPullParser.getEventType();
String id = "";
String name = "";
String version = "";
while (eventType != XmlPullParser.END_DOCUMENT) {
String nodeName = xmlPullParser.getName();
switch (eventType) {
//解析某個節點
case XmlPullParser.START_TAG:
if ("id".equals(nodeName)) {
id = xmlPullParser.nextText();
} else if ("name".equals(nodeName)) {
name = xmlPullParser.nextText();
}else if ("version".equals(nodeName)) {
version = xmlPullParser.nextText();
}
break;
//完成解析某個節點
case XmlPullParser.END_TAG:
if ("app".equals(nodeName)) {
Log.d(TAG, "xmlWithPull: id"+id);
Log.d(TAG, "xmlWithPull: name"+name);
Log.d(TAG, "xmlWithPull: version"+version);
}
break;
default:
break;
}
eventType = xmlPullParser.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
9.3.2 SAX解析方式
private void xmlWithSax(String xmlData){
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
MyHandler handler=new MyHandler();
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource(xmlData));
} catch (SAXException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
建立所需要的DefaultHandler
package com.dak.administrator.firstcode.net;
import android.util.Log;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* Created by Administrator on 2018/10/29.
*/
public class MyHandler extends DefaultHandler {
private static final String TAG = "MyHandler===";
String nodeName;
StringBuilder id,name, version;
/**
* 開始解析時呼叫
*
* @throws SAXException
*/
@Override
public void startDocument() throws SAXException {
id = new StringBuilder();
name = new StringBuilder();
version = new StringBuilder();
}
/**
* 完成整個xml解析後使用
*
* @throws SAXException
*/
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
/**
* 開始解析 某個節點時時呼叫
*
* @param uri
* @param localName
* @param qName
* @param attributes
* @throws SAXException
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
nodeName = localName;//記錄當前節點名
}
/**
* 完成解析某個節點呼叫
*
* @param uri
* @param localName
* @param qName
* @throws SAXException
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if ("app".equals(localName)) {
Log.d(TAG, "xmlWithPull: id"+id.toString().trim());
Log.d(TAG, "xmlWithPull: name"+name.toString().trim());
Log.d(TAG, "xmlWithPull: version"+version.toString().trim());
id.setLength(0);
name.setLength(0);
version.setLength(0);
}
}
/**
* 獲取某個節點內容時呼叫
* @param ch
* @param start
* @param length
* @throws SAXException
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
if ("id".equals(nodeName)) {
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);
}
}
}
9.4 解析JSON格式資料
9.4.1 使用JSONObject
/**
* 用 JSONObject 解析
* @param jsonData 需要解析的資料
*/
private void parseJSONWithJSONObject(String jsonData) {
try {
// 把需要解析的資料傳入到 JSONArray 物件中
JSONArray jsonArray = new JSONArray(jsonData);
for (int i = 0;i < jsonArray.length();i++){
JSONObject jsonObject = jsonArray.getJSONObject(i);
String id = jsonObject.getString("id");
String name = jsonObject.getString("name");
String sex = jsonObject.getString("sex");
Log.d("JSONObject解析", "id is "+id);
Log.d("JSONObject解析", "name is "+name);
Log.d("JSONObject解析", "sex is "+sex);
}
}catch (Exception e){
e.printStackTrace();
}
}
9.4.2 使用GSON
新增相關依賴
implementation 'com.google.code.gson:gson:2.8.2'
GSON 可以將一段 JSON 格式的字串自動對映成一個物件,從而不需要手動去編寫程式碼進行解析了。
比如解析一段 JSON 格式資料:
{"name":"Tom","age":20}
就可以定義一個 Person 類,並加入 name 和 age 兩欄位,然後只需呼叫如下程式碼就可以將 JSON 資料自動解析成一個 Person 物件:
Gson gson = new Gson();
Person person = gson.fromJson(jsonData,Person.class);
若解析一段 JSON 陣列會麻煩些,需要藉助 TypeToken 把期望解析成的資料型別傳入到 fromJson() 方法中:
List<Person> people = gson.fromJson(jsonData,new TypeToken<List<Person>>(){}.getType());
GSON 的基本用法就是這樣。下面來解析上面的 JSON 文字,首先新增一個 Student 類:
public class Student {
private String id;
private String name;
private String sex;
// Getter and Setter
. . .
}
接下來就非常簡單了,程式碼如下:
/**
* 用 GSON 解析
* @param jsonData
*/
private void parseJSONWithGSON(String jsonData){
Gson gson = new Gson();
List<Student>studentList = gson.fromJson(jsonData,new TypeToken<List<Student>>(){}.getType());
for (Student student:studentList){
Log.d("GSON解析", "id is "+student.getId());
Log.d("GSON解析", "name is "+student.getName());
Log.d("GSON解析", "sex is "+student.getSex());
}
}
9.5 網路程式設計的最佳實踐
這裡涉及到兩個問題:首先是網路連線的程式碼比較長,所以可以把它封裝在一個類裡面,然後設定一個靜態的方法,每次要進行網路連線的時候呼叫它就可以了;還有一個問題是由於網路連線需要開啟子執行緒,然而子執行緒又不能返回資料,所以需要設定回撥函式。
這裡首先建立一個介面,意思就是回撥函式:
public interface HttpCallbackListener {
void finish(String response);
void onError(Exception e);
}
然後定義HttpUtil,作為網路連線的通用類。
public class HttpUtil {
public static void sendHttpRequest(final String address, final HttpCallbackListener listener) {
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
try {
URL url = new URL(address);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setDoInput(true);
connection.setDoOutput(true);
InputStream in = connection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
listener.finish(sb.toString());
}
catch (Exception e) {
e.printStackTrace();
listener.onError(e);
}
finally {
if (connection != null) connection.disconnect();
}
}
}).start();
}
}
這裡就是傳送請求然後接收返回的資料的程式碼,增加的地方就是呼叫介面的函式。然後我們在MainActivity裡呼叫它,注意介面都是要先例項化之後才可以呼叫。
private void sendHttpRequestWithHttpUtil() {
String address = "http://www.baidu.com";
HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
@Override
public void onError(Exception e) {
Log.d("MainActivity", "連線失敗");
}
@Override
public void finish(String response) {
Message msg = new Message();
msg.obj = response;
handler.sendMessage(msg);
}
});
}
需要注意的地方是介面中的程式碼依然是在子執行緒中執行的,所以也不能直接修改UI。
來源:https://blog.csdn.net/xiaoliizi/article/details/50773218?locationNum=4
9.6 小結與點評
郭霖總結:
本章中我們主要學習了在Android 中使用HTTP協議來進行網路互動的知識,雖然Android中支援的網路通訊協議有很多種,但HTTP協議無疑是最常用的一種。 通常我們有兩種方式來發送HTTP請求,分別是HtpURLConnection和OktHtp,相信這兩種方式你都已經很好地掌握了。
接著我們又學習了XML和JSON格式資料的解析方式,因為伺服器響應給我們的資料般都是屬於這兩種格式的。無論是XML還是JSON,它們各自又擁有多種解析方式,這裡我們只是學習了最常用的幾種,如果以後你的工作中還需要用到其他的解析方式,可以自行去學習。
本章的最後同樣是最佳實踐環節,在這次的最佳實踐中,我們主要學習瞭如何利用Java的回撥機制來將伺服器響應的資料進行返回。其實除此之外,還有很多地方都可以使用到Java的回撥機制,希望你能舉一反三, 以後在其他地方需要用到回撥機制時都能夠靈活地使用。
在進行了一章多媒體和一章網路的相關知識學習後,你是否想起來Android四大元件中還剩一個沒有學過呢! 那麼下面就讓我們進人到Android 服務的學習旅程之中。
我的總結:
網路技術是必不可缺的一塊內容,在工作中,我們最常用的就是網路了,對於剛剛入職的小夥伴們,或許公司中有封裝好的框架,但是對於一些技術要點,一定要弄得明明白白的。