《第一行代码》读书笔记(八)

wshunli
2018-06-05 / 0 评论 / 74 阅读 / 正在检测是否收录...

《第一行代码》读书笔记 -- 网络编程

《第一行代码》读书笔记(一)-- 平台架构 (第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 文件做测试吧。

https://raw.githubusercontent.com/wshunli/wshunli.github.io/c28a64a898599442a10c9eee74fed8d54dc89e7f/api/posts.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

评论 (0)

取消