zoukankan      html  css  js  c++  java
  • 第 17 章 存储与加载本地文件

    请参考教材,全面理解和完成本章节内容... ...

    复制工程ch16,将工程目录改名为ch17.

    在手机上完全退出你的“陋习手记”App(不是把应用隐藏起来),再重新执行“陋习手记”App,哇!我的之前的手记哪里去了?

    几乎所有应用都需要有个地方存储数据。本章,我们将升级CriminalIntent应用,实现保存并加载存储在设备上的JSON文件数据。

    Android设备上的所有应用都拥有一个独立的文件系统,应用程序只能在自己的文件系统区域内存储和读取文件,不可以去其它地方访问,此区域被称为沙盒。所有的非代码文件都要保存在此,例如图像,图标,声音,映像,属性列表,文本文件等。将文件保存在沙盒中可阻止其他应用的访问、甚至是其他用户的私自窥探(当然,要是设备被root了的话,则用户可以随意获取任何数据)。

    提示

    沙盒也叫沙箱sandbox,且多用于计算机安全技术。其原理是通过重定向技术,把程序生成和修改的文件定向到自身文件夹中。沙盒中的程序和文件所做的任何改动对操作系统不会造成任何损失。这种技术被计算机技术人员广泛使用.

    每个应用的沙盒目录都是设备/data/data目录的子目录,且默认以应用包命名。例如,CriminalIntent应用的沙盒目录全路径为:/data/data/com.jet.criminalintent。

    好消息是,应用开发时,不必在内存中存放应用的沙盒目录路径。需要知道路径时,我们可直接调用Android API中的便利方法来获取它。

    除沙盒目录外,应用也可将文件保存在外部存储介质上,如常用的SD存储卡等。一般来说设备并不内置SD卡,因此需用户自行配置。虽然文件甚至整个应用都可以存储到SD卡上。但出于安全考虑,通常不推荐这么做。这其中最重要的一个因素就是,外部存储上的数据存取并不仅仅局限于应用本身,也就是说,任何人都可以读取、写入以及删除这些数据。

    17.1 CriminalIntent 应用的数据存取

    为应用添加数据持久存储功能主要涉及两大处理过程:将数据保存至文件系统、以及应用启动时重新加载保存的数据。每个处理过程又分为两个步骤。保存数据时,首先将数据转换为可保存格式(如JSON数据格式),然后将数据写入文件;读取数据时,则刚好相反,首先从文件中读取格式化的数据,然后将其解析为应用所需的内容。

    提示:

    JSONJavaScript Object NotationXML是一种比较流行的数据交换格式

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。JSON采用完全独立于语言的文本格式,这些特性使JSON成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成。

    采用JSON的格式的数据,采用一对"名称/值“的形式,简单如:

    {"firstName":"Brett","lastName":"McLaughlin","email":"aaaa"}

    如果用它保存数组,优势更明显,如:

    {

    "people":[

    {"firstName":"Brett","lastName":"McLaughlin","email":"aaaa"},

    {"firstName":"Jason","lastName":"Hunter","email":"bbbb"},

    {"firstName":"Elliotte","lastName":"Harold","email":"cccc"}

    ]

    }

    JSON适用于webservices接口服务的开发。Android SDK内置了标准的org.json类包,我们可以利用其中的类和方法来创建和解析JSON文件。要了解更多有关org.json包的信息,可查阅Android开发者文档。也可访问网址http://json.org,了解更多有关JSON数据交换格式的内容;而XML是另一种数据交换格式,可用来格式化数据以便写入文件。同样,Android提供了创建和解析XML文件的类和方法。

    CriminalIntent应用中,保存的数据格式是JSON。我们将使用Context类的I/O方法写入或读取文件。图17-1总体描绘了应如何实现CriminalIntent应用数据的保存与读取。

    image

    17-1 CriminalIntent应用的数据存取

    应用读取文件的最便捷方式是使用Context类的I/O方法。这些方法可以返回标准的Java类实例,如java.io.File和java.io.FileInputStream。(Context类几乎是所有关键应用组件的超类,常见的几个应用组件有:ApplicationActivityService。)

    17.1.1 保存crime数据到JSON文件

    在CriminalIntent应用中,CrimeLab类将负责触发数据的保存与加载,而创建和解析JSON数据的工作则交由新的CriminalIntentJSONSerializer类以及当前的Crime类处理。

    创建CriminalIntentJSONSerializer

    新的CriminalIntentJSONSerializer类负责读取Crime数组列表中的数据,然后进行数据格式转换,最后写入JSON文件。

    创建CriminalIntentJSONSerializer类,参照代码清单17-1输入实现代码(注: 代码中toJSON()方法会产生错误,因为稍后我们才会在Crime类中实现该方法。暂时忽略它)。

    代码清单17-1 编码实施CriminalIntentJSONSerializer

    image

    有个问题,需要讨论:

    Q:为什么不将对象序列化放到CrimeLab类中完成,而是单独放到CriminalIntentJSONSerializer类中实现?

    A:虽然对象序列化也可以直接在CrimeLab类中完成,但将其封装到独立的单元会有诸多优点:

    • 首先,对应用中其他代码部分的依赖度较低,独立封装类更容易进行单元测试。
    • 其次,CriminalIntentJSONSerializer类的构造方法可接受Context实例参数。这意味着该类不做任何修改就可以在多处复用,因为使用者只需实现任意一个Context类作为参数传入即可。

    saveCrimes(ArrayList<Crime>)方法中,应首先创建一个JSONArray数组对象。然后针对数组列表中的所有crime记录调用toJSON()方法,并将结果保存到JSONArray数组中。

    要打开文件并写入数据,需使用Context.openFileOutput()方法。该方法接受文件名以及文件操作模式参数,会自动将传入的文件名附加到应用沙盒文件目录路径之后,形成一个新路径,然后在新路径下创建并打开文件,等待数据写入。如选择手动获取私有文件目录并在其下创建和打开文件,记得总是使用Context.getFilesDir()替代方法。不过,如需创建不同使用权限的文件,还是少不了要使用openFileOutput()方法。

    新建文件打开后,即可使用标准的Java接口类来写入数据。这里,我们使用了java.io类包中的三个类:WriterOutputStreamOutputStreamWriter。整个过程大致描述如下:

    • 首先调用openFileOutput()方法获得OutputStream对象,
    • 然后用其创建一个新的OutputStreamWriter对象,
    • 最后调用OutputStreamWriter的写入方法写入数据。

    至于Java的Strings与最终写入文件的原始字节流之间的转换,则不必担心,OutputStreamWriter会搞定一切。

    实现Crime类的JSON序列化功能

    为了以JSON文件格式保存mCrimes数组,首先必须能以JSON文件格式保存单个Crime实例对象。在Crime.java中,添加下列常量,然后实现toJSON()方法,以JSON格式保存Crime对象,并返回可放入JSONArrayJSONObject类实例,如代码清单17-2所示。

    代码清单17-2 实现toJSON()方法(Crime.java)

    image

    以上代码中,使用JSONObject类中的方法,我们将Crime对象数据转换为可写入JSON文件的JSONObject对象数据。

    CrimeLab类中保存crime记录

    有了CriminalIntentJSONSerializer类以及支持JSON序列化的Crime类,现在可将crime列表转换为JSON格式,并保存到文件中。

    什么时“点”保存数据合适呢?适用于移动应用的一个普遍规则是:尽可能频繁地保存数据,尤其是用户数据修改行为发生时。既然修改crime记录后的数据更新都需CrimeLab类处理,那么最靠谱的就是在该类中将数据保存到文件中。

    如果数据保存过于频繁,会拖慢应用的运行,影响到用户的使用体验。我们的代码中,数据只要有更新,都是重新将全部crime数据写入文件中。考虑到CriminalIntent应用的规模,这样做不会太耗时。然而,对于超频繁数据保存的应用来说,应考虑采用某种方式只保存修改过的数据,而不是每次都保存全部数据,比如说使用SQLite数据库等。后几章我们将学习如何在应用中使用SQLite数据库。

    在CrimeLab.java中,在类构造方法里创建一个CriminalIntentJSONSerializer实例。然后再添加一个序列化crime对象的saveCrimes()方法。同时,为确认文件保存操作是否成功,再添加一些相应的日志记录代码。如代码清单17-3所示。

    代码清单17-3 在CrimeLab类中进行数据持久保存(CrimeLab.java)

    image

    简单起见,CriminalIntent应用只记录错误信息并输出至控制台。实际开发时,如文件保存失败,最好考虑采用某种方式直接提醒用户,例如,使用Toast或对话框。

    onPause()方法中保存应用数据

    应该在哪里调用saveCrimes()方法呢?onPause()生命周期方法是最安全的选择,如代码清单17-4所示。为什么不选择onStop()或者onDestroy()方法?前面我们讲过,操作系统需要回收内存时,会销毁暂停的activity,因此不应考虑它们,否则将会失去保存数据的机会。

    代码清单17-4 在onPause()方法中保存数据(CrimeFragment.java)

    image

    运行CriminalIntent应用。添加一两条crime记录,然后点击Home键暂停activity,保存crime记录列表到文件中。最后查看LogCat确认成功与否。

    17.1.2 从文件中读取crime数据

    现在我们来进行逆向操作,实现应用启动后,从文件中读取crime数据。首先,在Crime.java中,添加一个接受JSONObject对象的构造方法,如代码清单17-5所示。

    代码清单17-5 实现Crime(JSONObject)方法(Crime.java)

    image

    然后,在CriminalIntentJSONSerializer.java中,添加一个从文件中加载crime记录的loadCrimes()方法,如代码清单17-6所示。

    代码清单17-6 实现loadCrimes()方法(CriminalIntentJSONSerializer.java)

    image

    以上代码可以看到,联合使用Java、JSON类,以及ContextopenFileInput(...)方法,我们从文件中读取数据并转换为JSONObjects类型的string,然后再解析为JSONArray,接着再解析为ArrayList,最后返回获得的ArrayList

    注意,在finally代码块中,应调用reader.close()方法。这样,即使发生错误,也可以保证完成底层文件句柄的释放。

    最后,在CrimeLab的构造方法中,在应用首次访问单例对象时,代替总是创建空的crime数组列表,将crime数据加载到ArrayList数组列表中。在CrimeLab.java中,完成相应的代码修改,如代码清单17-7所示。

    代码清单17-7 加载crime记录(CrimeLab.java)

    image

    以上代码中,我们首先尝试加载crime数据。如加载失败,则新建一个空数组列表。

    现在,CriminalIntent应用可以保存应用启停间的数据了。我们可模拟一些不同场景进行测试。运行应用,添加几条crime记录,或修改现有记录,然后切换到其他应用,如网络浏览器。此时,CriminalIntent应用很可能会被操作系统关闭。重新启动它,检查更新的数据是否已保存。也可测试强制关闭应用,然后从Eclipse中重新启动应用的场景。

    现在可以放心地记录各种令人讨厌的办公室陋习了。既然应用已可靠地实现了数据持久化,后续CriminalIntent应用的功能升级过程中,可直接使用已保存的crime记录。从此,我们再也不需要在每次应用启动后,反反复复地添加crime记录了。

  • 相关阅读:
    django orm 以列表作为筛选条件进行查询
    申请Let's Encrypt通配符HTTPS证书
    redis集群部署及踩过的坑
    MySQL的索引是什么?怎么优化?
    Session管理之超时设置和强制下线
    在MySQL中使用explain查询SQL的执行计划
    基于Docker搭建MySQL主从复制
    这些年一直记不住的 Java I/O
    高并发大容量NoSQL解决方案探索
    php 如何生成静态页
  • 原文地址:https://www.cnblogs.com/jlxuqiang/p/4758656.html
Copyright © 2011-2022 走看看