标签:
请参考教材,全面理解和完成本章节内容... ...
复制工程ch16,将工程目录改名为ch17.
在手机上完全退出你的“陋习手记”App(不是把应用隐藏起来),再重新执行“陋习手记”App,哇!我的之前的手记哪里去了?
几乎所有应用都需要有个地方存储数据。本章,我们将升级CriminalIntent应用,实现保存并加载存储在设备上的JSON文件数据。
Android设备上的所有应用都有一个放置在沙盒中的文件目录。将文件保存在沙盒中可阻止其他应用的访问、甚至是其他用户的私自窥探(当然,要是设备被root了的话,则用户可以随意获取任何数据)。
提示
沙盒也叫沙箱sandbox,且多用于计算机安全技术。其原理是通过重定向技术,把程序生成和修改的文件定向到自身文件夹中。沙盒中的程序和文件所做的任何改动对操作系统不会造成任何损失。这种技术被计算机技术人员广泛使用.
每个应用的沙盒目录都是设备/data/data目录的子目录,且默认以应用包命名。例如,CriminalIntent应用的沙盒目录全路径为:/data/data/com.jet.criminalintent。
好消息是,应用开发时,不必在内存中存放应用的沙盒目录路径。需要知道路径时,我们可直接调用Android API中的便利方法来获取它。
除沙盒目录外,应用也可将文件保存在外部存储介质上,如常用的SD存储卡等。一般来说设备并不内置SD卡,因此需用户自行配置。虽然文件甚至整个应用都可以存储到SD卡上。但出于安全考虑,通常不推荐这么做。这其中最重要的一个因素就是,外部存储上的数据存取并不仅仅局限于应用本身,也就是说,任何人都可以读取、写入以及删除这些数据。
为应用添加数据持久存储功能主要涉及两大处理过程:将数据保存至文件系统、以及应用启动时重新加载保存的数据。每个处理过程又分为两个步骤。保存数据时,首先将数据转换为可保存格式(如JSON数据格式),然后将数据写入文件;读取数据时,则刚好相反,首先从文件中读取格式化的数据,然后将其解析为应用所需的内容。
提示:
JSON(JavaScript Object Notation)和XML是一种比较流行的数据交换格式。
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应用数据的保存与读取。
图17-1 CriminalIntent应用的数据存取
应用读取文件的最便捷方式是使用Context
类的I/O方法。这些方法可以返回标准的Java类实例,如java.io.File和java.io.FileInputStream。(Context
类几乎是所有关键应用组件的超类,常见的几个应用组件有:Application
、Activity
和Service
。)
在CriminalIntent应用中,CrimeLab
类将负责触发数据的保存与加载,而创建和解析JSON数据的工作则交由新的CriminalIntentJSONSerializer
类以及当前的Crime
类处理。
创建CriminalIntentJSONSerializer
类
新的CriminalIntentJSONSerializer
类负责读取Crime
数组列表中的数据,然后进行数据格式转换,最后写入JSON文件。
创建CriminalIntentJSONSerializer
类,参照代码清单17-1输入实现代码(注: 代码中toJSON()方法会产生错误,因为稍后我们才会在Crime
类中实现该方法。暂时忽略它)。
代码清单17-1 编码实施CriminalIntentJSONSerializer
类
有个问题,需要讨论:
Q:为什么不将对象序列化放到
CrimeLab
类中完成,而是单独放到CriminalIntentJSONSerializer
类中实现?A:虽然对象序列化也可以直接在
CrimeLab
类中完成,但将其封装到独立的单元会有诸多优点:
- 首先,对应用中其他代码部分的依赖度较低,独立封装类更容易进行单元测试。
- 其次,
CriminalIntentJSONSerializer
类的构造方法可接受Context
实例参数。这意味着该类不做任何修改就可以在多处复用,因为使用者只需实现任意一个Context类作为参数传入即可。
在saveCrimes(ArrayList<Crime>)
方法中,应首先创建一个JSONArray
数组对象。然后针对数组列表中的所有crime记录调用toJSON()
方法,并将结果保存到JSONArray
数组中。
要打开文件并写入数据,需使用Context.openFileOutput()
方法。该方法接受文件名以及文件操作模式参数,会自动将传入的文件名附加到应用沙盒文件目录路径之后,形成一个新路径,然后在新路径下创建并打开文件,等待数据写入。如选择手动获取私有文件目录并在其下创建和打开文件,记得总是使用Context.getFilesDir()
替代方法。不过,如需创建不同使用权限的文件,还是少不了要使用openFileOutput()
方法。
新建文件打开后,即可使用标准的Java接口类来写入数据。这里,我们使用了java.io
类包中的三个类:Writer
、OutputStream
和OutputStreamWriter
。整个过程大致描述如下:
openFileOutput()
方法获得OutputStream
对象, OutputStreamWriter
对象, OutputStreamWriter
的写入方法写入数据。至于Java的Strings
与最终写入文件的原始字节流之间的转换,则不必担心,OutputStreamWriter
会搞定一切。
实现Crime
类的JSON序列化功能
为了以JSON文件格式保存mCrimes
数组,首先必须能以JSON文件格式保存单个Crime实例对象。在Crime.java中,添加下列常量,然后实现toJSON()
方法,以JSON格式保存Crime
对象,并返回可放入JSONArray
的JSONObject
类实例,如代码清单17-2所示。
代码清单17-2 实现toJSON()
方法(Crime.java)
以上代码中,使用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)
简单起见,CriminalIntent应用只记录错误信息并输出至控制台。实际开发时,如文件保存失败,最好考虑采用某种方式直接提醒用户,例如,使用Toast
或对话框。
在onPause()
方法中保存应用数据
应该在哪里调用saveCrimes()
方法呢?onPause()
生命周期方法是最安全的选择,如代码清单17-4所示。为什么不选择onStop()
或者onDestroy()
方法?前面我们讲过,操作系统需要回收内存时,会销毁暂停的activity,因此不应考虑它们,否则将会失去保存数据的机会。
代码清单17-4 在onPause()
方法中保存数据(CrimeFragment.java)
运行CriminalIntent应用。添加一两条crime记录,然后点击Home键暂停activity,保存crime记录列表到文件中。最后查看LogCat确认成功与否。
现在我们来进行逆向操作,实现应用启动后,从文件中读取crime数据。首先,在Crime.java中,添加一个接受JSONObject
对象的构造方法,如代码清单17-5所示。
代码清单17-5 实现Crime(JSONObject)
方法(Crime.java)
然后,在CriminalIntentJSONSerializer.java中,添加一个从文件中加载crime记录的loadCrimes()方法,如代码清单17-6所示。
代码清单17-6 实现loadCrimes()
方法(CriminalIntentJSONSerializer.java)
以上代码可以看到,联合使用Java、JSON类,以及Context
的openFileInput(...)
方法,我们从文件中读取数据并转换为JSONObjects
类型的string
,然后再解析为JSONArray
,接着再解析为ArrayList
,最后返回获得的ArrayList
。
注意,在finally代码块中,应调用reader.close()
方法。这样,即使发生错误,也可以保证完成底层文件句柄的释放。
最后,在CrimeLab
的构造方法中,在应用首次访问单例对象时,代替总是创建空的crime数组列表,将crime数据加载到ArrayList
数组列表中。在CrimeLab.java中,完成相应的代码修改,如代码清单17-7所示。
代码清单17-7 加载crime记录(CrimeLab.java)
以上代码中,我们首先尝试加载crime数据。如加载失败,则新建一个空数组列表。
现在,CriminalIntent应用可以保存应用启停间的数据了。我们可模拟一些不同场景进行测试。运行应用,添加几条crime记录,或修改现有记录,然后切换到其他应用,如网络浏览器。此时,CriminalIntent应用很可能会被操作系统关闭。重新启动它,检查更新的数据是否已保存。也可测试强制关闭应用,然后从Eclipse中重新启动应用的场景。
现在可以放心地记录各种令人讨厌的办公室陋习了。既然应用已可靠地实现了数据持久化,后续CriminalIntent应用的功能升级过程中,可直接使用已保存的crime记录。从此,我们再也不需要在每次应用启动后,反反复复地添加crime记录了。
标签:
原文地址:http://www.cnblogs.com/jlxuqiang/p/4758656.html