android 数据存储和访问方式一:文件详解

 

数据存储和访问方式一:
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;
 }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值