惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LINUX DO - 热门话题
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Security @ Cisco Blogs
W
WeLiveSecurity
N
News and Events Feed by Topic
Security Archives - TechRepublic
Security Archives - TechRepublic
V2EX - 技术
V2EX - 技术
TaoSecurity Blog
TaoSecurity Blog
L
LINUX DO - 最新话题
Cloudbric
Cloudbric
S
Secure Thoughts
Recent Commits to openclaw:main
Recent Commits to openclaw:main
C
CXSECURITY Database RSS Feed - CXSecurity.com
Forbes - Security
Forbes - Security
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
The Blog of Author Tim Ferriss
博客园 - 【当耐特】
NISL@THU
NISL@THU
F
Full Disclosure
博客园_首页
Blog — PlanetScale
Blog — PlanetScale
腾讯CDC
B
Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
B
Blog RSS Feed
Jina AI
Jina AI
N
News | PayPal Newsroom
G
Google Developers Blog
P
Proofpoint News Feed
雷峰网
雷峰网
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Proofpoint News Feed
PCI Perspectives
PCI Perspectives
H
Heimdal Security Blog
GbyAI
GbyAI
Project Zero
Project Zero
Hacker News - Newest:
Hacker News - Newest: "LLM"
Y
Y Combinator Blog
S
Schneier on Security
Hacker News: Ask HN
Hacker News: Ask HN
S
Security Affairs
P
Privacy & Cybersecurity Law Blog
Attack and Defense Labs
Attack and Defense Labs
美团技术团队
T
Threat Research - Cisco Blogs
T
Threatpost
The Hacker News
The Hacker News
C
Cisco Blogs
Last Week in AI
Last Week in AI
Google DeepMind News
Google DeepMind News

博客园 - jlxuqiang

左手信仰,右手长剑 Cheat Sheet 《高级语言程序设计》课程指导 第 23 章 深入学习 intent 和任务 (选讲) 第 22 章 Master-Detail 用户界面 第 21 章 隐式 intent 12 信息2班 《基于Android的软件开发》课程成绩 test 13 信息2班 《安卓程序设计基础》课程成绩 第 20 章 相机 II:拍摄并处理照片 第 19 章 相机 I:取景器 第 18 章 上下文菜单与上下文操作模式 第 16 章 工具栏 第 15 章 应用本地化 第 14 章 fragment 的保留 第 13 章 使用 MediaPlayer 播放音频 第 12 章 对话框 第 11 章 使用 ViewPager 第 10 章 使用 fragment argument
第 17 章 存储与加载本地文件
jlxuqiang · 2015-08-25 · via 博客园 - jlxuqiang

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

复制工程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记录了。