数据存储和访问方式一:
MVC:V-layout\main.xml;C-Activity;M-业务层
①文件写入操作
//往手机自带的存储空间写入文件
FileOutputStream outStream = this.getContext().openFileOutput("test.txt", Context.MODE_PRIVATE);
outStream.write("西安网星".getBytes());
outStream.close();
openFileOutput(String name, int mode)方法的第一参数用于指定文件名称,不能包含路径分隔符“/”,如果文件不存在,Android应用会自动创建它。
创建的文件保存在/data/data/<package name>/files目录,如:/data/data/com.xawx.file/files/test.txt,
通过点击Eclipse菜单“Window”-“Show View”-“Other”,在对话窗口中展开Android文件夹,选择下面的File Explorer视图,
然后在File Explorer视图中展开/data/data/com.xawx.file/files目录就可以看到保存的文件test.txt。
openFileOutput(String name, int mode)方法的第二参数用于指定操作模式,有四种模式,分别为:
Context.MODE_PRIVATE = 0
Context.MODE_APPEND = 32768
Context.MODE_WORLD_READABLE = 1
Context.MODE_WORLD_WRITEABLE = 2
Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中,可以使用Context.MODE_APPEND。
Context.MODE_APPEND:创建的文件是私有数据,该模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。
MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。
如果希望该文件被其他应用读和写,可以这样写: openFileOutput("test.txt", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
android有一套自己的安全模型,当应用程序(.apk)在安装时系统就会分配给他一个userid,如果该应用要去访问其他资源比如文件的时候,就需要userid匹配。
默认情况下,任何应用创建的文件、SharedPreferences、数据库都应该是私有的(位于/data/data/<package name>/files),其他程序无法访问。
除非在创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE ,只有这样设置其他程序才能正确访问。
②文件读取操作
//从手机自带的存储空间里面读取文件
FileInputStream inStream = this.getContext().openFileInput("test.txt");
//定义缓冲区大小
byte [] buffer = new byte[1024];
int len = 0;
//内存输出流
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
//从输入流里面读取数据到缓冲区,如果读取到流的末尾返回-1,否则返回读取到的实际字节长度
while( (len = inStream.read(buffer)) != -1 ){
//往内存中写入数据
outStream.write(buffer, 0, len);
}
//获取内存中该文件的二进制数据
byte [] data = outStream.toByteArray();
outStream.close();
inStream.close();
Activity的getFilesDir()方法:返回/data/data/com.xawx.file/files文件对象
Activity的getCacheDir()方法:返回/data/data/com.xawx.file/cache文件对象,存放一些缓存数据
③往手机的SDCard里面写入文件
在模拟器中使用SDCard,你需要先创建一张SDCard卡(当然不是真的SDCard,只是镜像文件)。创建SDCard可以在Eclipse创建模拟器时随同创建,也可以使用DOS命令进行创建,在DOS窗口中进入android SDK安装路径的tools目录,输入以下命令创建一张容量为2G的SDCard,文件后缀可以随便取,建议使用.img:mksdcard 2048M D:\sdcard.img。
//判断SDCard是否存在,是否允许读取或者写入文件
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
//使用SDCard的绝对路径
//File file = new File("/mnt/sdcard", "test.txt");
//获取SDCard所在路径,建议使用此方式
File file = new File(Environment.getExternalStorageDirectory(), "test.txt");
FileOutputStream outStream = new FileOutputStream(file);
outStream.write("西安网星".getBytes());
outStream.close();
}
在程序中访问SDCard,需要申请访问SDCard的权限。
在AndroidManifest.xml中加入访问SDCard的权限,如下所示:
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 在SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
④解析XML文件
在Android平台上可以使用Simple API for XML(SAX)、Document Object Model(DOM)和Android附带的Pull解析器解析XML文件。
下面是要解析的XML文件:
文件名称:person.xml
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person id="1">
<name>张三</name>
<age>30</age>
</person>
<person id="2">
<name>李四</name>
<age>25</age>
</person>
</persons>
第一种方法:SAX是一个解析速度快并且占用内存少的XML解析器,非常适合用于Android等移动设备。SAX解析XML文件采用的是事件驱动,也就是说,它并不需要解析完整个文档,在按内容顺序解析文档的过程中,会判断当前读到的字符是否是合法XML语法中的某部分,如果符合就会触发事件。所谓事件,其实就是一些回调(callback)方法,这些方法(事件)定义在ContentHandler接口中。
下面是ContentHandler接口中一些常用的方法:
startDocument()
当遇到文档的开头的时候,调用这个方法,可以在其中做一些预处理的工作。
endDocument()
和上面的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。
startElement(String namespaceURI, String localName, String qName, Attributes atts)
当读到一个开始标签的时候,会触发这个方法。namespaceURI就是命名空间,localName是不带命名空间前缀的标签名,qName是带命名空间前缀的标签名,通过atts可以得到所有的属性名和相应的值。要注意的是SAX中一个重要的特点就是它的流式处理,当遇到一个标签的时候,它并不会记录下以前所碰到的标签,也就是说,在startElement()方法中,所有你所知道的信息,就是标签的名字和属性,至于标签的嵌套结构、上层标签的名字、是否有子元属等等其它与结构相关的信息,都是不得而知的,都需要你的程序来完成。这使得SAX在编程处理上没有DOM那么方便。
endElement(String uri, String localName, String name)
这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法。
characters(char[] ch, int start, int length)
这个方法用来处理在XML文件中读到的内容,第一个参数为整个XML文件的字符串内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch, start, length)就可以获取内容。
person.xml有几种类型的节点?一种是ElementNode,另一种是TextNode(注意<persons><person>之间的空白也是文本节点)。
其中<persons>、<person>这种节点就属于ElementNode,而zhangsan、30这种就属于TextNode。
<?xml version="1.0" encoding="UTF-8"?>---startDocument()
<persons>(--------------------------------startElement(String namespaceURI, String localName, String qName, Attributes atts)
)------characters(char[] ch, int start, int length)<person id="1">
<name>zhangsan</name>--------endElement(String uri, String localName, String name)
<age>30</age>
</person>
<person id="2">
<name>lisi</name>
<age>25</age>
</person>
</persons>
XML文件被SAX解析器载入,由于SAX解析是按照XML文件的顺序来解析,当读入<?xml.....>时,会调用startDocument()方法,当读入<persons>的时候,由于它是个ElementNode,所以会调用startElement(String uri, String localName, String qName, Attributes attributes) 方法,其中第二个参数就是节点的名称,注意:由于有些环境不一样,有时候第二个参数有可能为空,所以可以使用第三个参数,因此在解析前,先调用一下看哪个参数能用,第四个参数是这个节点的属性。由于我们不需要persons这个节点,所以从<person>这个节点开始,当读入时,调用startElement(....)方法,由于只有一个属性id,可以通过attributes.getValue(0)来得到,然后调用characters(char[] ch, int start, int length)方法,不要以为那里是空白,SAX解析器可不这么认为,SAX解析器会把它认为是一个TextNode,但是这个空白不是我们想要的数据,我们是想要<name>节点下的文本信息,这就要定义一个记录当前节点的名称的tag,在characters(.....)方法中,判断当前节点是不是name,如果是再取值,这样才能取到"zhangsan"。
public class SAXParsePersonService {
public ArrayList<Person> getPersons(InputStream is) throws Exception{
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
PersonHandler personHandler = new PersonHandler();
parser.parse(is, personHandler);
is.close();
return personHandler.getPersons();
}
private final class PersonHandler extends DefaultHandler{
private ArrayList<Person> persons = null;
private Person person = null;
private String tag = null;
@Override
public void startDocument() throws SAXException {
persons = new ArrayList<Person>();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if("person".equals(localName)){
person = new Person();
person.setId(new Integer(attributes.getValue(0)));
}
//记录当前节点的名称
tag = localName;
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if(tag != null){
String data = new String(ch, start, length);
if("name".equals(tag)){
person.setName(data);
}else if("age".equals(tag)){
person.setAge(new Short(data));
}
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if("person".equals(localName)){
persons.add(person);
person = null;
}
tag = null;
}
public List<Person> getPersons() {
return persons;
}
}
}
第二种方法:DOM解析XML文件时,会将XML文件的所有内容以文档树方式加载到内存中,然后允许使用DOM API遍历XML树、检索所需的数据。使用DOM操作XML的代码看起来是比较直观的,并且在编码方面比基于SAX的实现更加简单。因为DOM需要将XML文件的所有内容以文档树方式加载到内存中,所以对内存的消耗比较大,特别对于运行Android的移动设备来说,因为设备的资源比较宝贵,所以建议采用SAX来解析XML文件。当然,如果XML文件的内容比较小采用DOM也是可行的。
public class DOMParsePersonService {
public List<Person> getPersons(InputStream is) throws Exception{
List<Person> persons = new ArrayList<Person>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(is);
Element root = document.getDocumentElement();
NodeList personNodes = root.getElementsByTagName("person");
for(int i = 0; i < personNodes.getLength(); i++){
Person person = new Person();
Element personElement = (Element)personNodes.item(i);
Integer id = new Integer(personElement.getAttribute("id"));
person.setId(id);
NodeList personChilds = personElement.getChildNodes();
for(int j = 0; j < personChilds.getLength(); j++){
if(personChilds.item(j).getNodeType() == Node.ELEMENT_NODE){
Element personChild = (Element)personChilds.item(j);
if("name".equals(personChild.getNodeName())){
String name = personChild.getFirstChild().getNodeValue();
person.setName(name);
}else if("age".equals(personChild.getNodeName())){
Short age = new Short(personChild.getFirstChild().getNodeValue());
person.setAge(age);
}
}
}
persons.add(person);
}
is.close();
return persons;
}
}
第三种方法:使用Android内置的Pull解析器解析XML文件。Pull解析器的运行方式与SAX解析器相似。它提供了类似的事件,例如:开始元素和结束元素事件,使用parser.next()可以进入下一个元素并触发相应事件。事件将作为数值代码被发送,因此可以使用一个switch对感兴趣的事件进行处理。当元素开始解析时,调用parser.nextText()方法可以获取下一个Text类型节点的值。
Pull解析器的源码及文档下载网址:http://www.xmlpull.org/
public class PULLParsePersonService {
public static void savePersonsOutputStream(List<Person> persons, OutputStream outStream) throws Exception {
XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(outStream, "UTF-8");
serializer.startDocument("UTF-8", true);
serializer.startTag(null, "persons");
for(Person person : persons){
serializer.startTag(null, "person");
serializer.attribute(null, "id", person.getId().toString());
serializer.startTag(null, "name");
serializer.text(person.getName());
serializer.endTag(null, "name");
serializer.startTag(null, "age");
serializer.text(person.getAge().toString());
serializer.endTag(null, "age");
serializer.endTag(null, "person");
}
serializer.endTag(null, "persons");
serializer.endDocument();
outStream.flush();
outStream.close();
}
public static void savePersonsWriter(List<Person> persons, Writer writer) throws Exception {
XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(writer);
serializer.startDocument("UTF-8", true);
serializer.startTag(null, "persons");
for(Person person : persons){
serializer.startTag(null, "person");
serializer.attribute(null, "id", person.getId().toString());
serializer.startTag(null, "name");
serializer.text(person.getName());
serializer.endTag(null, "name");
serializer.startTag(null, "age");
serializer.text(person.getAge().toString());
serializer.endTag(null, "age");
serializer.endTag(null, "person");
}
serializer.endTag(null, "persons");
serializer.endDocument();
writer.flush();
writer.close();
}
public List<Person> getPersons(InputStream is) throws Exception{
List<Person> persons = null;
Person person = null;
XmlPullParser parser = Xml.newPullParser();
parser.setInput(is, "UTf-8");
//产生第一个事件
int eventType = parser.getEventType();
//判断是不是文档结束事件
while(eventType != XmlPullParser.END_DOCUMENT){
switch (eventType) {
//开始文档
case XmlPullParser.START_DOCUMENT:
persons = new ArrayList<Person>();
break;
//开始元素
case XmlPullParser.START_TAG:
//获取解析器当前指向的元素名称
String name = parser.getName();
if("person".equals(name)){
person = new Person();
//获取解析器当前指向的元素的第一个属性的值
person.setId(new Integer(parser.getAttributeValue(0)));
}
if(person != null){
if("name".equals(name)){
//获取解析器当前指向的元素的下一个文本节点的值
person.setName(parser.nextText());
}else if("age".equals(name)){
person.setAge(new Short(parser.nextText()));
}
}
break;
//结束元素
case XmlPullParser.END_TAG:
String pname = parser.getName();
if("person".equals(pname)){
persons.add(person);
person = null;
}
break;
default:
break;
}
//进入下一个事件
eventType = parser.next();
}
return persons;
}
}