《第一行代码》读书笔记 -- 网络编程
《第一行代码》读书笔记(一)-- 平台架构 (第1章)
《第一行代码》读书笔记(二)-- 应用组件之 Activity (第2、4章)
《第一行代码》读书笔记(三)-- 应用组件之 Service (第10章)
《第一行代码》读书笔记(四)-- 应用组件之 BroadcastReceiver (第5章)
《第一行代码》读书笔记(五)-- 应用组件之 ContentProvider (第7章)
《第一行代码》读书笔记(六)-- 数据存储方案 (第6章)
《第一行代码》读书笔记(七)-- 多媒体资源 (第8章)
《第一行代码》读书笔记(八)-- 网络编程 (第9章)
第8章 使用网络技术
WebView 的用法
WebView 也是 Android 的一个控件,用来显示一些网页,内容也比较多。
针对 WebView 的封装,可以使用 AgentWeb :
Justson/AgentWeb: AgentWeb is a powerful library based on Android WebView.
https://github.com/Justson/AgentWeb
优化首屏加载速度,可以使用 VasSonic :
Tencent/VasSonic: VasSonic is a lightweight and high-performance Hybrid framework developed by tencent VAS team, which is intended to speed up the first screen of websites working on Android and iOS platform.
https://github.com/Tencent/VasSonic
对于 WebView 有特殊要求的,可以使用一些浏览器内核:
Crosswalk - Embedding the Crosswalk Project
https://crosswalk-project.org/documentation/android/embedding_crosswalk.html
腾讯浏览服务:
http://x5.tencent.com/
本文只介绍 Android 系统 WebView 使用。
在 Android 4.4 以前使用基于 Android WebKit 的 WebView 实现;
在 Android 4.4 及以后使用基于 Chromium blink 的 WebView 实现;
从 Android 5.0 开始,Google 把 Chromium blink 内核作为 apk 单独从系统抽离出来。
WebView webView = findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient());
webView.loadUrl("https://html5test.com/");
这只是简单地显示网页,WebView 本身还有很多技巧。
使用 HTTP 协议访问网络
1、使用 HttpURLConnection
在 Android 上发送 HTTP 请求一般使用 HttpURLConnection 或者 HttpClient 。
不过 HttpClient 在 Android 6.0 已经废弃。
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
BufferedReader reader = null;
try {
URL url = new URL("https://www.wshunli.com/");
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
InputStream in = new BufferedInputStream(connection.getInputStream());
reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
Log.d(TAG, line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
上面是发送 GET 请求,发送 POST 请求:
connection.setRequestMethod("POST");
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes("username=wshunli&password=123456");
2、使用 OkHttp
在实际项目里面使用 HttpURLConnection 还是不太行的,往往使用一些开源的网络框架,比如 OkHttp 等等。
OkHttp:An HTTP & HTTP/2 client for Android and Java applications
https://github.com/square/okhttp
首先添加 OkHttp 依赖:
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
使用 OkHttp 发送 GET 请求:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://www.shunli.com")
.build();
try {
Response response = client.newCall(request).execute();
String responseData = response.body().string();
Log.d(TAG, "onCreate: " + responseData);
} catch (IOException e) {
e.printStackTrace();
}
整个过程清楚简洁。
···
RequestBody requestBody = new FormBody.Builder()
.add("username", "wshunli")
.add("password", "123456")
.build();
Request request = new Request.Builder()
.url("https://www.shunli.com")
.post(requestBody)
.build();
···
发送 POST 请求有点不太一样,要使用 RequestBody 传递数据。
这里只是简单介绍了 OkHttp 的使用,还有很多东西,后面再介绍。
解析 XML 格式数据
在网络传递的数据主要有两种: XML 和 JSON 。
这里我们使用本站的 RSS 订阅源数据。
https://www.wshunli.com/atom.xml
1、Pull 解析方式
本方法是将请求的 XML 字符串传入 XmlPullParser 对象实例,然后根据节点名字遍历解析 XML 内容。
new Thread(new Runnable() {
@Override
public void run() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://www.wshunli.com/atom.xml")
.build();
try {
Response response = client.newCall(request).execute();
String responseData = response.body().string();
parseXMLWithPull(responseData);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
private void parseXMLWithPull(String xmlData) {
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlData));
int eventType = xmlPullParser.getEventType();
String title = "";
String id = "";
String published = "";
String updated = "";
while (eventType != XmlPullParser.END_DOCUMENT) {
String nodeName = xmlPullParser.getName();
switch (eventType) {
case XmlPullParser.START_TAG:
if (nodeName.equals("title")) {
title = xmlPullParser.nextText();
} else if (nodeName.equals("id")) {
id = xmlPullParser.nextText();
} else if (nodeName.equals("published")) {
published = xmlPullParser.nextText();
} else if (nodeName.equals("updated")) {
updated = xmlPullParser.nextText();
}
break;
case XmlPullParser.END_TAG:
if (nodeName.equals("entry")) {
Log.d(TAG, "文章标题: " + title);
Log.d(TAG, "文章链接: " + id);
Log.d(TAG, "发布时间: " + published);
Log.d(TAG, "更新时间: " + updated);
}
break;
default:
break;
}
eventType = xmlPullParser.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
解析结果示例:
文章标题: 《第一行代码》读书笔记(七)
文章链接: https://www.wshunli.com/posts/941f84ed.html
发布时间: 2018-06-04T08:41:39.000Z
更新时间: 2018-06-04T15:14:46.188Z
文章标题: 《第一行代码》读书笔记(六)
文章链接: https://www.wshunli.com/posts/461ff372.html
发布时间: 2018-06-03T13:54:38.000Z
更新时间: 2018-06-04T15:14:46.188Z
2、SAX 解析方式
SAX 解析方式也是一种比较常用的方法,虽然比 Pull 方式复杂,但是语义更容易理解。
通常会新建一个类继承 DefaultHandler ,并重写其方法。
public class PostsHandler extends DefaultHandler {
@Override
public void startDocument() throws SAXException {
super.startDocument();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
}
其中 startDocument 和 endDocument 分别在开始、结束 XMl 接解析时调用。
类似 startElement 和 endElement 分别在开始、结束解析某个节点时调用。
而 characters 方法会在获取节点内容时调用,可能会调用多次,注意换行符也会解析出来。
public class PostsHandler extends DefaultHandler {
private static final String TAG = "PostsHandler";
private String nodeName;
private StringBuilder title;
private StringBuilder id;
private StringBuilder published;
private StringBuilder updated;
@Override
public void startDocument() throws SAXException {
title = new StringBuilder();
id = new StringBuilder();
published = new StringBuilder();
updated = 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 {
// 根据节点名称将内容添加到 StringBuilder 中
if (nodeName.equals("title")) {
title.append(ch, start, length);
} else if (nodeName.equals("id")) {
id.append(ch, start, length);
} else if (nodeName.equals("published")) {
published.append(ch, start, length);
} else if (nodeName.equals("updated")) {
updated.append(ch, start, length);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (localName.equals("entry")) {
Log.d(TAG, "文章标题: " + title.toString().trim());
Log.d(TAG, "文章链接: " + id.toString().trim());
Log.d(TAG, "发布时间: " + published.toString().trim());
Log.d(TAG, "更新时间: " + updated.toString().trim());
title.setLength(0);
id.setLength(0);
published.setLength(0);
updated.setLength(0);
}
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
}
和前面 Pull 解析功能是一样的。
private void parseXMLWithSAX(String xmlData) {
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
PostsHandler handler = new PostsHandler();
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource(new StringReader(xmlData)));
} catch (SAXException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
调用是使用 XMLReader 实例的 setContentHandler 方法。
解析 JSON 格式数据
感觉平常 JSON 数据用得更多一些,JSON 和 XML 相比主要有优势在于体积更小。
以前本站是支持生成 JSON 文件的,因为上传太慢了就取消了。
这里使用随便使用一个 JSON 文件做测试吧。
1、使用 JSONObject
解析 JSON 数据可以使用 JSONObject 或者 GSON 开源库,还有一些 Jackson 、FastJSON 都不错。
new Thread(new Runnable() {
@Override
public void run() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://raw.githubusercontent.com/wshunli/wshunli.github.io/c28a64a898599442a10c9eee74fed8d54dc89e7f/api/posts.json")
.build();
try {
Response response = client.newCall(request).execute();
String responseData = response.body().string();
parseJSONWithJSONObject(responseData);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
private void parseJSONWithJSONObject(String jsonData) {
try {
JSONObject jsonObject = new JSONObject(jsonData);
if (jsonObject.has("data")){
String data = jsonObject.getString("data");
JSONArray jsonArray = new JSONArray(data);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject object = jsonArray.getJSONObject(i);
String title = object.getString("title");
String path = object.getString("path");
String date = object.getString("date");
String updated = object.getString("updated");
Log.d(TAG, "文章标题: " + title);
Log.d(TAG, "文章链接: " + path);
Log.d(TAG, "发布时间: " + date);
Log.d(TAG, "更新时间: " + updated);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
也就是使用构造函数传入 JSON 字符串获得 JSONObject 对象实例。
如果遇到数组的使用 JSONArray 实例逐条遍历即可。
2、使用 GSON 库
google/gson: A Java serialization/deserialization library to convert Java Objects into JSON and back
https://github.com/google/gson
使用 GSON 解析 JSON 数据特别简单。
implementation 'com.google.code.gson:gson:2.8.4'
首先根据 JSON 数据定义 Posts 类。
public class Posts {
int total;
int pageSize;
int pageCount;
List<Article> data;
public static class Article{
String title;
String path;
String date;
String updated;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getUpdated() {
return updated;
}
public void setUpdated(String updated) {
this.updated = updated;
}
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getPageCount() {
return pageCount;
}
public void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
public List<Article> getData() {
return data;
}
public void setData(List<Article> data) {
this.data = data;
}
}
然后实例化 GSON 对象解析 JSON 字符串。
private void parseJSONWithGSON(String jsonDate) {
Gson gson = new Gson();
Posts posts = gson.fromJson(jsonDate, Posts.class);
List<Posts.Article> data = posts.getData();
for (Posts.Article article : data) {
Log.d(TAG, "文章标题: " + article.getTitle());
Log.d(TAG, "文章链接: " + article.getPath());
Log.d(TAG, "发布时间: " + article.getDate());
Log.d(TAG, "更新时间: " + article.getUpdated());
}
}
GSON 也就介绍到这里。
参考资料
1、Building Web Apps in WebView | Android Developers
https://developer.android.com/guide/webapps/webview
2、Android:最全面的 Webview 详解 - CSDN博客
https://blog.csdn.net/carson_ho/article/details/52693322
3、Getting Started: WebView-based Applications for Web Developers - Google Chrome
https://developer.chrome.com/multidevice/webview/gettingstarted
4、X5 浏览器内核调研报告 - 简书
https://www.jianshu.com/p/2a14d303308d
5、前端 WebView 指南之 Android 交互篇 - 怡红院落
https://imnerd.org/android-webview-and-js.html
6、在Android上使用JS引擎是一种什么样的体验? | 网易杭州前端技术部
https://neyoufan.github.io/2016/12/23/android/Android%20Js引擎/在Android上使用JS引擎是一种什么样的体验?/
到这里可能算又浏览了一遍 《第一行代码》 。
这次有些内容没有看,比如第 3、12 章的 UI 部分。
还有 第 11 章 基于位置的服务也没有看,因为前面专业 GIS SDK 接触很多了。
最后是第 13、14 章的进阶、实战部分,后面再了解。
评论 (0)