安卓学习日志 Day11 — JSON 解析

概述

我们将设计一个地震报告应用 (Quake Report),目的是让用户能够更好地获取身边或全世界有关地震活动的咨询。

有好的目标才能做出好的应用,那么体现出好目标的最好方式就是构建一个简单易用、目的明确的用户界面。

在这里插入图片描述

USGS 网站

探索 USGS 站点,并了解如何请求应用所需要的数据。同时了解数据以何种结构返回给我们,并建立一个方案,在代码中以编程方式将数据提取出来。

以用户身份浏览

打开 USGS 站点,在 Spreadsheet Format 页面中我们可以看到 CSV 格式的地震数据,这是一种看起来类似表格的数据格式。

用户可以从该页面下载到 地震数据的 CSV 文件,下载之后可以使用 Excel(工作表)来打开并查看,数据的的结构确实如表格一样清晰,易于查看。

在这里插入图片描述

以开发者身份浏览

而在 GeoJSON Summary Format 页面中 USGS 为开发者提供了一个具有特殊格式的数据,叫做 JSON。开发者可以通过 USGS 规定的格式来请求需要的数据(请求方法可查看文档 API Documentation - Earthquake Catalog)。

比如在浏览器中访问 这个地址 将会得到下图所示的响应:

在这里插入图片描述

导入项目

当某位 Android 专业开发者加入新项目时,通常,团队可能已经有了一个可继续处理的应用。 最好知道如何读取现有代码库,因为通常不需要从头创建新应用。

我已经在 GitHub 创建好了一个 Quake Report 应用 的初始版本,使用 Git 命令克隆该项目:

git clone -b starting-point https://github.com/HEY-BLOOD/QuakeReport.git

然后按以下说明将 Quake Report 项目导入计算机上的 Android Studio 中:

  • 打开 Android Studio。
  • 选择“文件”(File) >“导入项目”(Import Project),然后选择 “Quake Report” 文件夹。 导入项目需要一些时间。
  • 成功导入该应用后,在 Android 设备(手机、平板电脑或模拟器)上运行该应用。其外观类似于 以下屏幕截图。

在这里插入图片描述

在 Android Studio 中,浏览代码库找到 Quake Report 应用,并熟悉项目文件。

可以看出在 Quake Report 应用的初始版本只有一个页面,该页面只显示了地震位置信息(place)的类别,接下来更改项目使这个页面的列表中每一项都显示一次地震的 震级(magnitude)、位置(place)、时间(time)。

这需要使用到 ListView 和 自定义适配器的知识,我在 安卓学习日志 Day03 — Custom Adapter 已经说明过了,更改后的 Quake Report 应用应该和下方的截图相似。(更改前后的代码对比可以在 此链接 查看)

在这里插入图片描述

JSON

Json 文本的键和值以冒号分隔,同时每个键/值对以逗号分隔。 JSON 支持可在大多数编程语言中找到的基本数据类型, 如,数字、字符串、布尔值、数组和对象。

要处理 JSON 格式的数据需要使用到 Java 中的 JSON 对象,具体方法请参考链接 Android JSON Parsing with Examples - Tutlane

JSON 处理

那么接下来,试着对真实的地震数据(JSON 格式)进行处理,并显示到 QuakeReport 应用当中。这里我借助了 QueryUtils.java 类 的代码,使用该类 只需更改 文件顶部的 SAMPLE_JSON_RESPONSE String 常量中定义的 JSON 字符串(暂时采用硬编码的方法获取 JSON 字符串) 以及 QueryUtils.extractEarthquakes() 方法中剩下的 TODO 部分即可,以下是 需要执行的一些伪代码:

 将 SAMPLE_JSON_RESPONSE String 转换为 JSONObject
 提取 "features" JSONArray
 依次循环数组中的每个特征
 获取 i 位置的 earthquake JSON 对象
 获取 "properties" JSON 对象
 针对震级提取 "mag"
 针对位置提取 "place"
 针对时间提取 "time"
 通过震级、位置和时间创建 Earthquake java 对象
 将地震添加到地震列表

改 EarthquakeActivity 调用 QueryUtils.extractEarthquakes() 从 JSON 响应获取 Earthquake 对象的列表。

在 EarthquakeActivity 中,不再创建伪地震列表,而是替换为以下代码:

ArrayList<Earthquake> earthquakes = QueryUtils.extractEarthquakes();

然后更改 QueryUtils.extractEarthquakes() 方法中剩下的 TODO 部分如下:

    /**
     * Return a list of {@link Earthquake} objects that has been built up from
     * parsing a JSON response.
     */
    public static ArrayList<Earthquake> extractEarthquakes() {
   

        // Create an empty ArrayList that we can start adding earthquakes to
        ArrayList<Earthquake> earthquakes = new ArrayList<>();

        // Try to parse the SAMPLE_JSON_RESPONSE. If there's a problem with the way the JSON
        // is formatted, a JSONException exception object will be thrown.
        // Catch the exception so the app doesn't crash, and print the error message to the logs.
        try {
   
            // TODO: Parse the response given by the SAMPLE_JSON_RESPONSE string and
            // build up a list of Earthquake objects with the corresponding data.
            JSONObject rootJsonObj = new JSONObject(SAMPLE_JSON_RESPONSE);
            JSONArray featuresArray = rootJsonObj.getJSONArray("features");
            for (int i = 0; i < featuresArray.length(); i++) {
   
                JSONObject feature = featuresArray.getJSONObject(i);
                JSONObject properties = feature.getJSONObject("properties");
                String earthquakeMag = properties.getDouble("mag") + "";
                String earthquakePlace = properties.getString("place");
                String earthquakeTime = properties.getLong("time") + "";

                Earthquake earthquake = new Earthquake(earthquakeMag, earthquakePlace, earthquakeTime);

                earthquakes.add(earthquake);
            }
        } catch (JSONException e) {
   
            // If an error is thrown when executing any of the above statements in the "try" block,
            // catch the exception here, so the app doesn't crash. Print a log message
            // with the message from the exception.
            Log.e("QueryUtils", "Problem parsing the earthquake JSON results", e);
        }

        // Return the list of earthquakes
        return earthquakes;
    }

更改完成后试着运行一下应用,这时 地震信息的列表 应该如下图类似(更改前后的代码对比参考 此链接):

在这里插入图片描述

Unix 时间

首先来更新 发生地震时显示相关信息的方式。以应用目前的状态,可以 轻松看到每次地震的震级和位置,但时间 则是一长串数字。第一次地震的时间是 1454124312220,这种格式称为 Unix 时间,此格式对我们没有帮助。

Unix 时间,将时间描述为 从英国 1970 年 1 月 1 日 0 时 0 分 0 秒起至现在的总毫秒数 (更技术性的叫法为协调世界时间)。

我们想要显示的时间信息 应包含具体的年月日,所以需要将此 Unix 时间转换成更易懂的日期 和时间,但这将是一个非常复杂的命题。我们可能希望 按照用户的本地时区显示时间,但时区极为复杂,另外,根据用户在 世界所处的位置,日期的书写方式也会有所不同。

日期格式设置

很庆幸,我们无需自己处理日期格式。Java 中有 一个很奇妙的名为 SimpleDateFormat 的类,知晓所有关于时区的信息, 以及知晓如何按照世界的不同地区书写日期,它会 处理这些难题。

接下来,使用 SimpleDateFormat 的类将 QuakeReport 应用中 的时间格式更改为 人类可读的 日期时间格式(如,Jan 3, 2020)。

由于 SimpleDateFormat.format() 方法需要一个Date 对象,而该对象需要长整型数据作为输入,因此我们应从 JSON 响应将地震时间提取为长整型 (long) 数据类型(而不是 String)。长整型是 Java 中的 8 个原始数据类型之一。

将 QueryUtils.extractEarthquakes() 方法修改为 从 JSON 响应中将地震时间提取为 “long” 数据类型。

// Extract the value for the key called "time"
long time = properties.getLong("time");

这会引发错误,因为 Earthquake 构造函数不 接受长整型数据类型作为第三输入参数。因此,我们需要 对 Earthquake 类进行一些更改。

更改全局变量的数据类型,对其进行重命名,以使名称 更能描述其中所存储的信息。

在 Earthquake.java 中:

    /** Time of the earthquake */
    private long timeInMilliseconds;

修改 Earthquake 构造函数,使其采用长整型数据类型表示时间 并更新 Javadoc 注释。

在 Earthquake.java 中:

    /**
     * 构造一个新的 {@link Earthquake} 对象。
     *
     * @param mag   表示地震的震级(大小)
     * @param place 表示地震的城市位置
     * @param timeInMilliseconds  表示地震发生时以毫秒(根据 Epoch)计的时间
     */
    public Earthquake(String mag, String place, long timeInMilliseconds) {
   
        this.mag = mag;
        this.place = place;
        this.timeInMilliseconds = timeInMilliseconds;
    }

更新公共 getter 方法,以便返回长整型数据类型。

在 Earthquake.java 中:

    /**
     * 返回地震的时间。
     */
    public long getTimeInMilliseconds() {
   
        return timeInMilliseconds;
    }

当 EarthquakeAdapter 创建每次地震的列表项时, 适配器必须将以毫秒为单位的时间转换为相应 格式的日期和时间。现在,有 2 个 TextView 来分别显示日期 和时间,因此我们需要修改 earthquake_item.xml 布局以添加另外一个 TextView 并为其提供一个合适的视图 ID,并将地震发生的 日期 和 时间 纵向排列。

更改 earthquake_item.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="@dimen/dp_16">

    <TextView
        android:id="@+id/mag_text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        tools:text="8.0" />

    <TextView
        android:id="@+id/place_text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_weight="3"
        tools:text="Wenchuan Sichuan" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:orientation="vertical">

        <TextView
            android:id="@+id/date_text"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:gravity="right"
            tools:text="May 12, 2008" />

        <TextView
            android:id="@+id/time_text"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:gravity="right"
            android:textAllCaps="true"
            tools:text="3:00
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值