Android入门学习笔记(一)|基础知识|文件数据存储读取|解析XML
2014-03-05 11:37
1081 查看
屏幕分辨率
HVGA:320×480
QVGA:240x320
WQVGA400:240X400
WQVAG432:240X432
WVGA800: 480X800
WVGA854: 480X854
Android应用程序架构
src/ java源代码存放目录
gen/ 自动生成目录 gen 目录中存放所有由Android开发工具自动生成的文件。目录中最重要的就是R.java文件。 这个文件由Android开发工具自动产生的。Android开发工具会自动根据你放入res目录的xml界面文件、图标与常量,同步更新修改R.java文件。正因为R.java文件是由开发工具自动生成的,所以我们应避免手工修改R.java。R.java在应用中起到了字典的作用,它包含了界面、图标、常量等各种资源的id,通过R.java,应用可以很方便地找到对应资源。另外编绎器也会检查R.java列表中的资源是否被使用到,没有被使用到的资源不会编绎进软件中,这样可以减少应用在手机占用的空间。
res/ 资源(Resource)目录 在这个目录中我们可以存放应用使用到的各种资源,如xml界面文件,图片或数据。具体请看ppt下方备注栏。
assets资源目录 Android除了提供/res目录存放资源文件外,在/assets目录也可以存放资源文件,而且/assets目录下的资源文件不会在R.java自动生成ID,所以读取/assets目录下的文件必须指定文件的路径,如:file:///android_asset/xxx.3gp
AndroidManifest.xml 项目清单文件 这个文件列出了应用程序所提供的功能,以后你开发好的各种组件需要在该文件中进行配置,如果应用使用到了系统内置的应用(如电话服务、互联网服务、短信服务、GPS服务等等),你还需在该文件中声明使用权限。
default.properties 项目环境信息,一般是不需要修改此文件。
res/drawable 专门存放png、jpg等图标文件。在代码中使用getResources().getDrawable(resourceId)获取该目录下的资源。
res/layout 专门存放xml界面文件,xml界面文件和HTML文件一样,主要用于显示用户操作界面。
res/values 专门存放应用使用到的各种类型数据。不同类型的数据存放在不同的文件中,如下:
· strings.xml 定义字符串和数值,在Activity中使用getResources().getString(resourceId) 或getResources().getText(resourceId)取得资源。它的作用和struts中的国际化资源文件一样。
XML语言:
<?xml version="1.0" encoding="UTF-8"?> <resources> <string name="itcast">传智播客</string> </resources>
· arrays.xml 定义数组。
XML语言:
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="colors"> <item>red</item><item>yellow</item> <item>green</item> <item>blue</item> </string-array> </resources>
·colors.xml 定义颜色和颜色字串数值,你可以在Activity中使用getResources().getDrawable(resourceId) 以及getResources().getColor(resourceId)取得这些资源。例子如下:
XML语言:
?xml version="1.0" encoding="UTF-8"?> <resources> <color name="contents_text">#ff0000</color></resources>
· dimens.xml 定义尺寸数据,在Activity中使用getResources().getDimension(resourceId) 取得这些资源
XML语言:
<?xml version="1.0" encoding="UTF-8"?> <resources> <dimen name="key_height">50dip</dimen> </resources>
· styles.xml 定义样式。
XML语言:
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="itcastText" parent="@style/Text"> <itemname="android:textSize">18sp</item> <item name="android:textColor">#0066FF</item> </style> </resources>
res/anim/ 存放定义动画的XML文件。
res/xml/ 在Activity中使用getResources().getXML()读取该目录下的XML资源文件。
res/raw/ 该目录用于存放应用使用到的原始文件,如音效文件等。编译软件时,这些数据不会被编译,它们被直接加入到程序安装包里。 为了在程序中使用这些资源,你可以调用getResources().openRawResource(ID) , 参数ID形式:R.raw.somefilename。
Android中的显示单位
px (pixels)像素 一般HVGA代表320x480像素,这个用的比较多。 dip或dp (device independent pixels)设备独立像素 这个和设备硬件有关,一般为了支持WVGA、HVGA和QVGA 推荐使用这个,不依赖像素。 sp (scaled pixels — best for text size)比例像素 主要处理字体的大小,可以根据系统的字体自适应。 除了上面三个显示单位,下面还有几个不太常用: in (inches)英寸 mm (millimeters)毫米 pt (points)点,1/72英寸
为了适应不同分辨率,不同的像素密度,推荐使用dip ,文字使用sp。
新开启一个模拟器:
在Dos窗口中进入android SDK安装路径的tools目录,输入以下命令再开启一个Android模拟器: emulator -data itcast 注:itcast为用户数据存取文件,如果该文件不存在,默认在tools目录创建该文件
“尚未注册网络”错误信息的解决办法
打开Android模拟器时,出现无信号,拔打电话或发短信时,提示“尚未注册网络”错误信息的解决方案如下。
场景一:你的电脑没有连接上互联网,同时也没有在局域网。 解决办法:右键点击网上邻居,选择"属性",在网络连接窗口中右键点击"本地连接",选择"属性",设置TCP/IP属性如下: IP地址:192.168.1.100 子网掩码:255.255.255.0 默认网关:192.168.1.100 首选DNS服务器:192.168.1.100
场景二:你的电脑没有连接上互联网,但在局域网。 解决办法:右键点击网上邻居,选择"属性",在网络连接窗口中右键点击"本地连接",选择"属性",设置TCP/IP属性如下: IP地址:设置成你所在局域网的IP,如:192.168.1.100 子网掩码:设置成你所在局域网的掩码,如:255.255.255.0 默认网关:设置成你所在局域网的网关,一般网关的IP格式为:*.*.*.1,如:192.168.1.1 首选DNS服务器:设置成你所在局域网的路由器IP,一般路由器的IP格式为:*.*.*.1,如:192.168.1.1
最后一种解决方案是:让你的电脑连接上互联网。
发送短信示例:
Java语言:
SmsManager smsManager = SmsManager.getDefault();
PendingIntent sentIntent = PendingIntent.getBroadcast(MainActivity.this, 0, new Intent(), 0);
//如果字数超过70,需拆分成多条短信发送
if (strContent.length() > 70) {
List<String> msgs = smsManager.divideMessage(strContent);
for (String msg : msgs) {
smsManager.sendTextMessage(strNo, null, msg, sentIntent, null);
}
} else {
smsManager.sendTextMessage(strNo, null, strContent, sentIntent, null);
}
Toast.makeText(MainActivity.this, "短信发送完成", Toast.LENGTH_LONG).show();
对应用进行单元测试
在实际开发中,开发android软件的过程需要不断地进行测试。而使用Junit测试框架,侧是正规的Android开发的必用技术,在Junit中可以得到组件,可以模拟发送事件和检测程序处理的正确性。
第一步:首先在AndroidManifest.xml中加入下面红色斜体代码:
XML语言:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.itcast.action“
android:versionCode="1“ android:versionName="1.0"> <application android:icon="@drawable/icon"android:label="@string/app_name"> <uses-library android:name="android.test.runner" /> .... </application><uses-sdk android:minSdkVersion="6" /> <instrumentationandroid:name="android.test.InstrumentationTestRunner"
android:targetPackage="cn.itcast.action" android:label="Tests for My App" /> </manifest>
上面targetPackage指定的包要和应用的package相同。
第二步:编写单元测试代码(选择要测试的方法,右键点击“Run As”--“Android Junit Test” ):
Java语言:
import android.test.AndroidTestCase;
import android.util.Log;
public class XMLTest extends AndroidTestCase {
public void testSomething() throws Throwable {
Assert.assertTrue(1 + 1 == 3);
}
}
使用文件进行数据存储
首先给大家介绍使用文件如何对数据进行存储,Activity提供了openFileOutput()方法可以用于把数据输出到文件中,具体的实现过程与在J2SE环境中保存数据到文件中是一样的。
Java语言:
public class FileActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { ...FileOutputStream outStream = this.openFileOutput("itcast.txt", Context.MODE_PRIVATE); outStream.write("传智播客".getBytes()); outStream.close(); } }
openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。创建的文件保存在/data/data/<package name>/files目录,如: /data/data/cn.itcast.action/files/itcast.txt ,通过点击Eclipse菜单“Window”-“Show View”-“Other”,在对话窗口中展开android文件夹,选择下面的File Explorer视图,然后在File Explorer视图中展开/data/data/<package
name>/files目录就可以看到该文件。
openFileOutput()方法的第二参数用于指定操作模式,有四种模式,分别为:
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("itcast.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 ,只有这样其他程序才能正确访问。
读取文件内容
如果要打开存放在/data/data/<package name>/files目录应用私有的文件,可以使用Activity提供openFileInput()方法。或者直接使用文件的绝对路径:
Java语言:
FileInputStream inStream = this.getContext().openFileInput("itcast.txt"); Log.i("FileTest",readInStream(inStream));
File file = new File("/data/data/cn.itcast.action/files/itcast.txt"); FileInputStream inStream = newFileInputStream(file); Log.i("FileTest", readInStream(inStream));
readInStream方法:
Java语言:
public static String readInStream(FileInputStream inStream){
try {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length = -1;
while((length = inStream.read(buffer)) != -1 ){
outStream.write(buffer, 0, length);
}
outStream.close();
inStream.close();
return outStream.toString();
} catch (IOException e) {
Log.i("FileTest", e.getMessage());
}
return null;
}
注意:上面文件路径中的“cn.itcast.action”为应用所在包,当你在编写代码时应替换为你自己应用使用的包。 对于私有文件只能被创建该文件的应用访问,如果希望文件能被其他应用读和写,可以在创建文件时,指定Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限。 Activity还提供了getCacheDir()和getFilesDir()方法:
getCacheDir()方法用于获取/data/data/<package name>/cache目录
getFilesDir()方法用于获取/data/data/<package name>/files目录
把文件存放在SDCard
使用Activity的openFileOutput()方法保存文件,文件是存放在手机空间上,一般手机的存储空间不是很大,存放些小文件还行,如果要存放像视频这样的大文件,是不可行的。对于像视频这样的大文件,我们可以把它存放在SDCard。 SDCard是干什么的?你可以把它看作是移动硬盘或U盘。 在模拟器中使用SDCard,你需要先创建一张SDCard卡(当然不是真的SDCard,只是镜像文件)。创建SDCard可以在Eclipse创建模拟器时随同创建,也可以使用DOS命令进行创建,如下: 在Dos窗口中进入android
SDK安装路径的tools目录,输入以下命令创建一张容量为2G的SDCard,文件后缀可以随便取,建议使用.img: mksdcard 2048M D:\AndroidTool\sdcard.img
在程序中访问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"/>
要往SDCard存放文件,程序必须先判断手机是否装有SDCard,并且可以进行读写。 注意:访问SDCard必须在AndroidManifest.xml中加入访问SDCard的权限
Java语言:
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录
File saveFile = new File(sdCardDir, “itcast.txt”);
FileOutputStream outStream = new FileOutputStream(saveFile);
outStream.write("传智播客".getBytes());
outStream.close();
}
Environment.getExternalStorageState()方法用于获取SDCard的状态,如果手机装有SDCard,并且可以进行读写,那么方法返回的状态等于Environment.MEDIA_MOUNTED。 Environment.getExternalStorageDirectory()方法用于获取SDCard的目录,当然要获取SDCard的目录,你也可以这样写:
Java语言:
File sdCardDir = new File("/mnt/sdcard"); //获取SDCard目录
File saveFile = new File(sdCardDir, "itcast.txt");
//上面两句代码可以合成一句:
File saveFile = new File("/mnt/sdcard/itcast.txt");
FileOutputStream outStream = new FileOutputStream(saveFile);
outStream.write("传智播客test".getBytes());
outStream.close();
使用SAX或者DOM或者pull解析XML文件
在Android平台上可以使用Simple API for XML(SAX) 、 Document Object Model(DOM)和Android附带的pull解析器解析XML文件。 下面是本例子要解析的XML文件: 文件名称:itcast.xml
XML语言:
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person id="23">
<name>李明</name>
<age>30</age>
</person>
<person id="20">
<name>李向梅</name>
<age>25</age>
</person>
</persons>
例子定义了一个javabean用于存放上面解析出来的xml内容, 这个javabean为Person:
Java语言:
public class Person {
private Integer id;
private String name;
private Short age;public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Short getAge() {
return age;
}
public void setAge(Short age) {
this.age = age;
}
}
使用SAX读取XML文件
SAX是一个解析速度快并且占用内存少的xml解析器,非常适合用于Android等移动设备。 SAX解析XML文件采用的是事件驱动,也就是说,它并不需要解析完整个文档,在按内容顺序解析文档的过程中,SAX会判断当前读到的字符是否合法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文件中读到的内容,第一个参数为文件的字符串内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch,start,length)就可以获取内容。
XML语言:
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person id="23">
<name>李明</name>
<age>30</age>
</person>
<person id="20">
<name>李向梅</name>
<age>25</age>
</person>
</persons>
解析itcast.xml触发的事件为:
读到的标签及内容 触发事件
{文档开始} startDocument()
<persons> startElement(, "persons", null, "{Attributes}")
"\n\t" characters("<persons>...</persons>",
"12", "2")
<person> startElement(, "person", null, "{Attributes}")
"\n\t\t" characters("<persons>...</persons>",
"31", "3")
<name> startElement(, "name", null, "{Attributes}")
"李明" characters("<persons>...</persons>",
"40", "2")
</name> endElement("", "name", null)
"\n\t\t" characters("<persons>...</persons>",
"50", "3")
<age> startElement(, "age", null, "{Attributes}")
"30" characters("<persons>...</persons>",
"58", "2")
</age> endElement("", "age", null)
"\n\t" characters("<persons>...</persons>",
"67", "2")
</person> endElement("", "person", null)
"\n\t" characters("<persons>...</persons>",
"79", "2")
<person> startElement(, "person", null, "{Attributes}")
"\n\t\t" characters("<persons>...</persons>",
"98", "3")
<name> startElement(, "name", null, "{Attributes}")
"李向梅" characters("<persons>...</persons>",
"107", "3")
</name> endElement("", "name", null)
"\n\t\t" characters("<persons>...</persons>",
"118", "3")
<age> startElement(, "age", null, "{Attributes}")
"25" characters("<persons>...</persons>",
"126", "2")
</age> endElement("", "age", null)
"\n\t" characters("<persons>...</persons>",
"135", "2")
</person> endElement("", "person", null)
"\n" characters("<persons>...</persons>",
"147", "1")
</persons> endElement("", "persons", null)
{文档结束} endDocument()
只要为SAX提供实现ContentHandler接口的类,那么该类就可以得到通知事件(实际上就是SAX调用了该类中的回调方法)。因为ContentHandler是一个接口,在使用的时候可能会有些不方便,因此,SAX还为其制定了一个Helper类:DefaultHandler,它实现了ContentHandler接口,但是其所有的方法体都为空,在实现的时候,你只需要继承这个类,然后重写相应的方法即可。使用SAX解析itcast.xml的代码如下:
Java语言:
public static List<Person> readXML(InputStream inStream) {
try {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser saxParser = spf.newSAXParser(); //创建解析器
//设置解析器的相关特性,http://xml.org/sax/features/namespaces = true 表示开启命名空间特性
//saxParser.setProperty("http://xml.org/sax/features/namespaces",true);
XMLContentHandler handler = new XMLContentHandler();
saxParser.parse(inStream, handler);
inStream.close();
return handler.getPersons();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
SAX 支持已内置到JDK1.5中,你无需添加任何的jar文件。XMLContentHandler的代码实现:
Java语言:
import java.util.ArrayList;
import java.util.List;import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;import cn.itcast.xml.domain.Person;public class XMLContentHandlerextends DefaultHandler {
private List<Person> persons = null;
private Person currentPerson;
private String tagName = null;//当前解析的元素标签public List<Person> getPersons() {
return persons;
}
/*
* 接收文档的开始的通知。
*/
@Override public void startDocument() throws SAXException {
persons = new ArrayList<Person>();
}
/*
* 接收字符数据的通知。
*/
@Override public void characters(char[] ch, int start, int length) throws SAXException {
if(tagName!=null){
String data = new String(ch, start, length);
if(tagName.equals("name")){
this.currentPerson.setName(data);
}else if(tagName.equals("age")){
this.currentPerson.setAge(Short.parseShort(data));
}
}
}
/*
* 接收元素开始的通知。
* 参数意义如下:
* namespaceURI:元素的命名空间
* localName :元素的本地名称(不带前缀)
* qName :元素的限定名(带前缀)
* atts :元素的属性集合
*/
@Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts)throws SAXException {
if(localName.equals("person")){
currentPerson = new Person();
currentPerson.setId(Integer.parseInt(atts.getValue("id")));
}
this.tagName = localName;
}
/*
* 接收文档的结尾的通知。
* 参数意义如下:
* uri :元素的命名空间
* localName :元素的本地名称(不带前缀)
* name :元素的限定名(带前缀)
*
*/
@Override public void endElement(String uri, String localName, String name) throws SAXException {
if(localName.equals("person")){
persons.add(currentPerson);
currentPerson = null;
}
this.tagName = null;
}
}
使用DOM读取XML文件
除了使用 SAX可以解析XML文件,大家也可以使用熟悉的DOM来解析XML文件。 DOM解析XML文件时,会将XML文件的所有内容以文档树方式存放在内存中,然后允许您使用DOM API遍历XML树、检索所需的数据。使用DOM操作XML的代码看起来是比较直观的,并且在编码方面比基于SAX的实现更加简单。但是,因为DOM需要将XML文件的所有内容以文档树方式存放在内存中,所以内存的消耗比较大,特别对于运行Android的移动设备来说,因为设备的资源比较宝贵,所以建议还是采用SAX来解析XML文件,当然,如果XML文件的内容比较小采用DOM也是可行的。
代码如下:
Java语言:
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;import cn.itcast.xml.domain.Person;
/**
* 使用Dom解析xml文件
*
*/
public class DomXMLReader {public static List<Person> readXML(InputStream inStream) {
List<Person> persons = new ArrayList<Person>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document dom = builder.parse(inStream);
Element root = dom.getDocumentElement();
NodeList items = root.getElementsByTagName("person");//查找所有person节点
for (int i = 0; i < items.getLength(); i++) {
Person person = new Person();
//得到第一个person节点
Element personNode = (Element) items.item(i);
//获取person节点的id属性值
person.setId(new Integer(personNode.getAttribute("id")));
//获取person节点下的所有子节点(标签之间的空白节点和name/age元素)
NodeList childsNodes = personNode.getChildNodes();
for (int j = 0; j < childsNodes.getLength(); j++) {
Node node = (Node) childsNodes.item(j);
//判断是否为元素类型
if(node.getNodeType() == Node.ELEMENT_NODE){
Element childNode = (Element) node;
//判断是否name元素
if ("name".equals(childNode.getNodeName())) {
//获取name元素下Text节点,然后从Text节点获取数据 person.setName(childNode.getFirstChild().getNodeValue());
} else if (“age”.equals(childNode.getNodeName())) {
person.setAge(new Short(childNode.getFirstChild().getNodeValue()));
}
}
}
persons.add(person);
}
inStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return persons;
}
使用Pull解析器读取XML文件
除了可以使用 SAX和DOM解析XML文件,大家也可以使用Android内置的Pull解析器解析XML文件。 Pull解析器的运行方式与 SAX 解析器相似。它提供了类似的事件,如:开始元素和结束元素事件,使用parser.next()可以进入下一个元素并触发相应事件。事件将作为数值代码被发送,因此可以使用一个switch对感兴趣的事件进行处理。当元素开始解析时,调用parser.nextText()方法可以获取下一个Text类型节点的值。 使用Pull解析器读取itcast.xml的代码如下
Pull解析器的源码及文档下载网址:http://www.xmlpull.org/
Java语言:
import org.xmlpull.v1.XmlPullParser;
import android.util.Xml;
import cn.itcast.xml.domain.Person;public class PullXMLReader {public static List<Person> readXML(InputStreaminStream) {
XmlPullParser parser = Xml.newPullParser();
try {
parser.setInput(inStream, "UTF-8");
int eventType = parser.getEventType();
Person currentPerson = null;
List<Person> persons = null;
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 (name.equalsIgnoreCase("person")) {
currentPerson = new Person();
currentPerson.setId(new Integer(parser.getAttributeValue(null, "id")));
} else if (currentPerson != null) {
if (name.equalsIgnoreCase("name")) {
currentPerson.setName(parser.nextText());//
如果后面是Text节点,即返回它的值
} else if (name.equalsIgnoreCase("age")) {
currentPerson.setAge(new Short(parser.nextText()));
}
}
break;
case XmlPullParser.END_TAG://结束元素事件
if (parser.getName().equalsIgnoreCase("person") && currentPerson != null) {
persons.add(currentPerson);
currentPerson = null;
}
break;
}
eventType = parser.next();
}
inStream.close();
return persons;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
使用Pull解析器生成XML文件
有些时候,我们需要生成一个XML文件,生成XML文件的方法有很多,如:可以只使用一个StringBuilder组拼XML内容,然后把内容写入到文件中;或者使用DOM API生成XML文件,或者也可以使用pull解析器生成XML文件,这里推荐大家使用Pull解析器。 使用Pull解析器生成一个与itcast.xml文件内容相同的myitcast.xml文件,代码如下。
Java语言:
//生成XML文件:
File xmlFile = new File("myitcast.xml"); FileOutputStream outStream = new FileOutputStream(xmlFile);OutputStreamWriter outStreamWriter = new OutputStreamWriter(outStream, "UTF-8"); BufferedWriter writer =new BufferedWriter(outStreamWriter); writeXML(persons, writer); writer.flush(); writer.close();
//如果只想得到生成的xml字符串内容,可以使用StringWriter:
StringWriter writer = new StringWriter(); writeXML(persons, writer); String content = writer.toString();
Java语言:
public static String writeXML(List<Person> persons, Writer writer){
XmlSerializer serializer = Xml.newSerializer();
try {
serializer.setOutput(writer);
serializer.startDocument("UTF-8", true);
//第一个参数为命名空间,如果不使用命名空间,可以设置为null
serializer.startTag("", "persons");
for (Person person : persons){
serializer.startTag("", "person");
serializer.attribute("", "id", person.getId().toString());
serializer.startTag("", "name");
serializer.text(person.getName());
serializer.endTag("", "name");
serializer.startTag("", "age");
serializer.text(person.getAge().toString());
serializer.endTag("", "age");
serializer.endTag("", "person");
}
serializer.endTag("", "persons");
serializer.endDocument();
return writer.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
使用SharedPreferences进行数据存储
很多时候我们开发的软件需要向用户提供软件参数设置功能,例如我们常用的QQ,用户可以设置是否允许陌生人添加自己为好友。对于软件配置参数的保存,如果是window软件通常我们会采用ini文件进行保存,如果是j2se应用,我们会采用properties属性文件或者xml进行保存。如果是Android应用,我们最适合采用什么方式保存软件配置参数呢?Android平台给我们提供了一个SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数。使用SharedPreferences保存数据,其背后是用xml文件存放数据,文件存放在/data/data/<package
name>/shared_prefs目录下:
Java语言:
SharedPreferences sharedPreferences = getSharedPreferences("itcast", Context.MODE_PRIVATE);
Editor editor = sharedPreferences.edit();//获取编辑器
editor.putString("name", "传智播客");
editor.putInt("age", 4);
editor.commit();//提交修改
生成的itcast.xml文件内容如下:
XML语言:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="name">传智播客</string>
<int name="age" value="4" />
</map>
因为SharedPreferences背后是使用xml文件保存数据,getSharedPreferences(name,mode)方法的第一个参数用于指定该文件的名称,名称不用带后缀,后缀会由Android自动加上。方法的第二个参数指定文件的操作模式,共有四种操作模式,这四种模式前面介绍使用文件方式保存数据时已经讲解过。如果希望SharedPreferences背后使用的xml文件能被其他应用读和写,可以指定Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限。
另外Activity还提供了另一个getPreferences(mode)方法操作SharedPreferences,这个方法默认使用当前类不带包名的类名作为文件的名称。
访问SharedPreferences中的数据
Java语言:
//访问SharedPreferences中的数据代码如下:
SharedPreferences sharedPreferences = getSharedPreferences("itcast", Context.MODE_PRIVATE);
//getString()第二个参数为缺省值,如果preference中不存在该key,将返回缺省值
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", 1);//如果访问其他应用中的Preference,前提条件是:该preference创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE权限。
//如:有个<package name>为cn.itcast.action的应用使用下面语句创建了preference。
getSharedPreferences("itcast", Context.MODE_WORLD_READABLE);
//其他应用要访问上面应用的preference,首先需要创建上面应用的Context,然后通过Context 访问preference ,
//访问preference时会在应用所在包下的shared_prefs目录找到preference :
Context otherAppsContext = createPackageContext("cn.itcast.action", Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences sharedPreferences = otherAppsContext.getSharedPreferences("itcast",Context.MODE_WORLD_READABLE);
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", 0);
//如果不通过创建Context访问其他应用的preference,也可以以读取xml文件方式直接访问其他应用preference对应的xml文件,如:
File xmlFile = new File(“/data/data/<package name>/shared_prefs/itcast.xml”);//<package
name>应替换成应用的包名
HVGA:320×480
QVGA:240x320
WQVGA400:240X400
WQVAG432:240X432
WVGA800: 480X800
WVGA854: 480X854
Android应用程序架构
src/ java源代码存放目录
gen/ 自动生成目录 gen 目录中存放所有由Android开发工具自动生成的文件。目录中最重要的就是R.java文件。 这个文件由Android开发工具自动产生的。Android开发工具会自动根据你放入res目录的xml界面文件、图标与常量,同步更新修改R.java文件。正因为R.java文件是由开发工具自动生成的,所以我们应避免手工修改R.java。R.java在应用中起到了字典的作用,它包含了界面、图标、常量等各种资源的id,通过R.java,应用可以很方便地找到对应资源。另外编绎器也会检查R.java列表中的资源是否被使用到,没有被使用到的资源不会编绎进软件中,这样可以减少应用在手机占用的空间。
res/ 资源(Resource)目录 在这个目录中我们可以存放应用使用到的各种资源,如xml界面文件,图片或数据。具体请看ppt下方备注栏。
assets资源目录 Android除了提供/res目录存放资源文件外,在/assets目录也可以存放资源文件,而且/assets目录下的资源文件不会在R.java自动生成ID,所以读取/assets目录下的文件必须指定文件的路径,如:file:///android_asset/xxx.3gp
AndroidManifest.xml 项目清单文件 这个文件列出了应用程序所提供的功能,以后你开发好的各种组件需要在该文件中进行配置,如果应用使用到了系统内置的应用(如电话服务、互联网服务、短信服务、GPS服务等等),你还需在该文件中声明使用权限。
default.properties 项目环境信息,一般是不需要修改此文件。
res/drawable 专门存放png、jpg等图标文件。在代码中使用getResources().getDrawable(resourceId)获取该目录下的资源。
res/layout 专门存放xml界面文件,xml界面文件和HTML文件一样,主要用于显示用户操作界面。
res/values 专门存放应用使用到的各种类型数据。不同类型的数据存放在不同的文件中,如下:
· strings.xml 定义字符串和数值,在Activity中使用getResources().getString(resourceId) 或getResources().getText(resourceId)取得资源。它的作用和struts中的国际化资源文件一样。
XML语言:
<?xml version="1.0" encoding="UTF-8"?> <resources> <string name="itcast">传智播客</string> </resources>
· arrays.xml 定义数组。
XML语言:
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="colors"> <item>red</item><item>yellow</item> <item>green</item> <item>blue</item> </string-array> </resources>
·colors.xml 定义颜色和颜色字串数值,你可以在Activity中使用getResources().getDrawable(resourceId) 以及getResources().getColor(resourceId)取得这些资源。例子如下:
XML语言:
?xml version="1.0" encoding="UTF-8"?> <resources> <color name="contents_text">#ff0000</color></resources>
· dimens.xml 定义尺寸数据,在Activity中使用getResources().getDimension(resourceId) 取得这些资源
XML语言:
<?xml version="1.0" encoding="UTF-8"?> <resources> <dimen name="key_height">50dip</dimen> </resources>
· styles.xml 定义样式。
XML语言:
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="itcastText" parent="@style/Text"> <itemname="android:textSize">18sp</item> <item name="android:textColor">#0066FF</item> </style> </resources>
res/anim/ 存放定义动画的XML文件。
res/xml/ 在Activity中使用getResources().getXML()读取该目录下的XML资源文件。
res/raw/ 该目录用于存放应用使用到的原始文件,如音效文件等。编译软件时,这些数据不会被编译,它们被直接加入到程序安装包里。 为了在程序中使用这些资源,你可以调用getResources().openRawResource(ID) , 参数ID形式:R.raw.somefilename。
Android中的显示单位
px (pixels)像素 一般HVGA代表320x480像素,这个用的比较多。 dip或dp (device independent pixels)设备独立像素 这个和设备硬件有关,一般为了支持WVGA、HVGA和QVGA 推荐使用这个,不依赖像素。 sp (scaled pixels — best for text size)比例像素 主要处理字体的大小,可以根据系统的字体自适应。 除了上面三个显示单位,下面还有几个不太常用: in (inches)英寸 mm (millimeters)毫米 pt (points)点,1/72英寸
为了适应不同分辨率,不同的像素密度,推荐使用dip ,文字使用sp。
新开启一个模拟器:
在Dos窗口中进入android SDK安装路径的tools目录,输入以下命令再开启一个Android模拟器: emulator -data itcast 注:itcast为用户数据存取文件,如果该文件不存在,默认在tools目录创建该文件
“尚未注册网络”错误信息的解决办法
打开Android模拟器时,出现无信号,拔打电话或发短信时,提示“尚未注册网络”错误信息的解决方案如下。
场景一:你的电脑没有连接上互联网,同时也没有在局域网。 解决办法:右键点击网上邻居,选择"属性",在网络连接窗口中右键点击"本地连接",选择"属性",设置TCP/IP属性如下: IP地址:192.168.1.100 子网掩码:255.255.255.0 默认网关:192.168.1.100 首选DNS服务器:192.168.1.100
场景二:你的电脑没有连接上互联网,但在局域网。 解决办法:右键点击网上邻居,选择"属性",在网络连接窗口中右键点击"本地连接",选择"属性",设置TCP/IP属性如下: IP地址:设置成你所在局域网的IP,如:192.168.1.100 子网掩码:设置成你所在局域网的掩码,如:255.255.255.0 默认网关:设置成你所在局域网的网关,一般网关的IP格式为:*.*.*.1,如:192.168.1.1 首选DNS服务器:设置成你所在局域网的路由器IP,一般路由器的IP格式为:*.*.*.1,如:192.168.1.1
最后一种解决方案是:让你的电脑连接上互联网。
发送短信示例:
Java语言:
SmsManager smsManager = SmsManager.getDefault();
PendingIntent sentIntent = PendingIntent.getBroadcast(MainActivity.this, 0, new Intent(), 0);
//如果字数超过70,需拆分成多条短信发送
if (strContent.length() > 70) {
List<String> msgs = smsManager.divideMessage(strContent);
for (String msg : msgs) {
smsManager.sendTextMessage(strNo, null, msg, sentIntent, null);
}
} else {
smsManager.sendTextMessage(strNo, null, strContent, sentIntent, null);
}
Toast.makeText(MainActivity.this, "短信发送完成", Toast.LENGTH_LONG).show();
对应用进行单元测试
在实际开发中,开发android软件的过程需要不断地进行测试。而使用Junit测试框架,侧是正规的Android开发的必用技术,在Junit中可以得到组件,可以模拟发送事件和检测程序处理的正确性。
第一步:首先在AndroidManifest.xml中加入下面红色斜体代码:
XML语言:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.itcast.action“
android:versionCode="1“ android:versionName="1.0"> <application android:icon="@drawable/icon"android:label="@string/app_name"> <uses-library android:name="android.test.runner" /> .... </application><uses-sdk android:minSdkVersion="6" /> <instrumentationandroid:name="android.test.InstrumentationTestRunner"
android:targetPackage="cn.itcast.action" android:label="Tests for My App" /> </manifest>
上面targetPackage指定的包要和应用的package相同。
第二步:编写单元测试代码(选择要测试的方法,右键点击“Run As”--“Android Junit Test” ):
Java语言:
import android.test.AndroidTestCase;
import android.util.Log;
public class XMLTest extends AndroidTestCase {
public void testSomething() throws Throwable {
Assert.assertTrue(1 + 1 == 3);
}
}
使用文件进行数据存储
首先给大家介绍使用文件如何对数据进行存储,Activity提供了openFileOutput()方法可以用于把数据输出到文件中,具体的实现过程与在J2SE环境中保存数据到文件中是一样的。
Java语言:
public class FileActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { ...FileOutputStream outStream = this.openFileOutput("itcast.txt", Context.MODE_PRIVATE); outStream.write("传智播客".getBytes()); outStream.close(); } }
openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。创建的文件保存在/data/data/<package name>/files目录,如: /data/data/cn.itcast.action/files/itcast.txt ,通过点击Eclipse菜单“Window”-“Show View”-“Other”,在对话窗口中展开android文件夹,选择下面的File Explorer视图,然后在File Explorer视图中展开/data/data/<package
name>/files目录就可以看到该文件。
openFileOutput()方法的第二参数用于指定操作模式,有四种模式,分别为:
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("itcast.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 ,只有这样其他程序才能正确访问。
读取文件内容
如果要打开存放在/data/data/<package name>/files目录应用私有的文件,可以使用Activity提供openFileInput()方法。或者直接使用文件的绝对路径:
Java语言:
FileInputStream inStream = this.getContext().openFileInput("itcast.txt"); Log.i("FileTest",readInStream(inStream));
File file = new File("/data/data/cn.itcast.action/files/itcast.txt"); FileInputStream inStream = newFileInputStream(file); Log.i("FileTest", readInStream(inStream));
readInStream方法:
Java语言:
public static String readInStream(FileInputStream inStream){
try {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length = -1;
while((length = inStream.read(buffer)) != -1 ){
outStream.write(buffer, 0, length);
}
outStream.close();
inStream.close();
return outStream.toString();
} catch (IOException e) {
Log.i("FileTest", e.getMessage());
}
return null;
}
注意:上面文件路径中的“cn.itcast.action”为应用所在包,当你在编写代码时应替换为你自己应用使用的包。 对于私有文件只能被创建该文件的应用访问,如果希望文件能被其他应用读和写,可以在创建文件时,指定Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限。 Activity还提供了getCacheDir()和getFilesDir()方法:
getCacheDir()方法用于获取/data/data/<package name>/cache目录
getFilesDir()方法用于获取/data/data/<package name>/files目录
把文件存放在SDCard
使用Activity的openFileOutput()方法保存文件,文件是存放在手机空间上,一般手机的存储空间不是很大,存放些小文件还行,如果要存放像视频这样的大文件,是不可行的。对于像视频这样的大文件,我们可以把它存放在SDCard。 SDCard是干什么的?你可以把它看作是移动硬盘或U盘。 在模拟器中使用SDCard,你需要先创建一张SDCard卡(当然不是真的SDCard,只是镜像文件)。创建SDCard可以在Eclipse创建模拟器时随同创建,也可以使用DOS命令进行创建,如下: 在Dos窗口中进入android
SDK安装路径的tools目录,输入以下命令创建一张容量为2G的SDCard,文件后缀可以随便取,建议使用.img: mksdcard 2048M D:\AndroidTool\sdcard.img
在程序中访问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"/>
要往SDCard存放文件,程序必须先判断手机是否装有SDCard,并且可以进行读写。 注意:访问SDCard必须在AndroidManifest.xml中加入访问SDCard的权限
Java语言:
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录
File saveFile = new File(sdCardDir, “itcast.txt”);
FileOutputStream outStream = new FileOutputStream(saveFile);
outStream.write("传智播客".getBytes());
outStream.close();
}
Environment.getExternalStorageState()方法用于获取SDCard的状态,如果手机装有SDCard,并且可以进行读写,那么方法返回的状态等于Environment.MEDIA_MOUNTED。 Environment.getExternalStorageDirectory()方法用于获取SDCard的目录,当然要获取SDCard的目录,你也可以这样写:
Java语言:
File sdCardDir = new File("/mnt/sdcard"); //获取SDCard目录
File saveFile = new File(sdCardDir, "itcast.txt");
//上面两句代码可以合成一句:
File saveFile = new File("/mnt/sdcard/itcast.txt");
FileOutputStream outStream = new FileOutputStream(saveFile);
outStream.write("传智播客test".getBytes());
outStream.close();
使用SAX或者DOM或者pull解析XML文件
在Android平台上可以使用Simple API for XML(SAX) 、 Document Object Model(DOM)和Android附带的pull解析器解析XML文件。 下面是本例子要解析的XML文件: 文件名称:itcast.xml
XML语言:
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person id="23">
<name>李明</name>
<age>30</age>
</person>
<person id="20">
<name>李向梅</name>
<age>25</age>
</person>
</persons>
例子定义了一个javabean用于存放上面解析出来的xml内容, 这个javabean为Person:
Java语言:
public class Person {
private Integer id;
private String name;
private Short age;public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Short getAge() {
return age;
}
public void setAge(Short age) {
this.age = age;
}
}
使用SAX读取XML文件
SAX是一个解析速度快并且占用内存少的xml解析器,非常适合用于Android等移动设备。 SAX解析XML文件采用的是事件驱动,也就是说,它并不需要解析完整个文档,在按内容顺序解析文档的过程中,SAX会判断当前读到的字符是否合法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文件中读到的内容,第一个参数为文件的字符串内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch,start,length)就可以获取内容。
XML语言:
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person id="23">
<name>李明</name>
<age>30</age>
</person>
<person id="20">
<name>李向梅</name>
<age>25</age>
</person>
</persons>
解析itcast.xml触发的事件为:
读到的标签及内容 触发事件
{文档开始} startDocument()
<persons> startElement(, "persons", null, "{Attributes}")
"\n\t" characters("<persons>...</persons>",
"12", "2")
<person> startElement(, "person", null, "{Attributes}")
"\n\t\t" characters("<persons>...</persons>",
"31", "3")
<name> startElement(, "name", null, "{Attributes}")
"李明" characters("<persons>...</persons>",
"40", "2")
</name> endElement("", "name", null)
"\n\t\t" characters("<persons>...</persons>",
"50", "3")
<age> startElement(, "age", null, "{Attributes}")
"30" characters("<persons>...</persons>",
"58", "2")
</age> endElement("", "age", null)
"\n\t" characters("<persons>...</persons>",
"67", "2")
</person> endElement("", "person", null)
"\n\t" characters("<persons>...</persons>",
"79", "2")
<person> startElement(, "person", null, "{Attributes}")
"\n\t\t" characters("<persons>...</persons>",
"98", "3")
<name> startElement(, "name", null, "{Attributes}")
"李向梅" characters("<persons>...</persons>",
"107", "3")
</name> endElement("", "name", null)
"\n\t\t" characters("<persons>...</persons>",
"118", "3")
<age> startElement(, "age", null, "{Attributes}")
"25" characters("<persons>...</persons>",
"126", "2")
</age> endElement("", "age", null)
"\n\t" characters("<persons>...</persons>",
"135", "2")
</person> endElement("", "person", null)
"\n" characters("<persons>...</persons>",
"147", "1")
</persons> endElement("", "persons", null)
{文档结束} endDocument()
只要为SAX提供实现ContentHandler接口的类,那么该类就可以得到通知事件(实际上就是SAX调用了该类中的回调方法)。因为ContentHandler是一个接口,在使用的时候可能会有些不方便,因此,SAX还为其制定了一个Helper类:DefaultHandler,它实现了ContentHandler接口,但是其所有的方法体都为空,在实现的时候,你只需要继承这个类,然后重写相应的方法即可。使用SAX解析itcast.xml的代码如下:
Java语言:
public static List<Person> readXML(InputStream inStream) {
try {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser saxParser = spf.newSAXParser(); //创建解析器
//设置解析器的相关特性,http://xml.org/sax/features/namespaces = true 表示开启命名空间特性
//saxParser.setProperty("http://xml.org/sax/features/namespaces",true);
XMLContentHandler handler = new XMLContentHandler();
saxParser.parse(inStream, handler);
inStream.close();
return handler.getPersons();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
SAX 支持已内置到JDK1.5中,你无需添加任何的jar文件。XMLContentHandler的代码实现:
Java语言:
import java.util.ArrayList;
import java.util.List;import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;import cn.itcast.xml.domain.Person;public class XMLContentHandlerextends DefaultHandler {
private List<Person> persons = null;
private Person currentPerson;
private String tagName = null;//当前解析的元素标签public List<Person> getPersons() {
return persons;
}
/*
* 接收文档的开始的通知。
*/
@Override public void startDocument() throws SAXException {
persons = new ArrayList<Person>();
}
/*
* 接收字符数据的通知。
*/
@Override public void characters(char[] ch, int start, int length) throws SAXException {
if(tagName!=null){
String data = new String(ch, start, length);
if(tagName.equals("name")){
this.currentPerson.setName(data);
}else if(tagName.equals("age")){
this.currentPerson.setAge(Short.parseShort(data));
}
}
}
/*
* 接收元素开始的通知。
* 参数意义如下:
* namespaceURI:元素的命名空间
* localName :元素的本地名称(不带前缀)
* qName :元素的限定名(带前缀)
* atts :元素的属性集合
*/
@Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts)throws SAXException {
if(localName.equals("person")){
currentPerson = new Person();
currentPerson.setId(Integer.parseInt(atts.getValue("id")));
}
this.tagName = localName;
}
/*
* 接收文档的结尾的通知。
* 参数意义如下:
* uri :元素的命名空间
* localName :元素的本地名称(不带前缀)
* name :元素的限定名(带前缀)
*
*/
@Override public void endElement(String uri, String localName, String name) throws SAXException {
if(localName.equals("person")){
persons.add(currentPerson);
currentPerson = null;
}
this.tagName = null;
}
}
使用DOM读取XML文件
除了使用 SAX可以解析XML文件,大家也可以使用熟悉的DOM来解析XML文件。 DOM解析XML文件时,会将XML文件的所有内容以文档树方式存放在内存中,然后允许您使用DOM API遍历XML树、检索所需的数据。使用DOM操作XML的代码看起来是比较直观的,并且在编码方面比基于SAX的实现更加简单。但是,因为DOM需要将XML文件的所有内容以文档树方式存放在内存中,所以内存的消耗比较大,特别对于运行Android的移动设备来说,因为设备的资源比较宝贵,所以建议还是采用SAX来解析XML文件,当然,如果XML文件的内容比较小采用DOM也是可行的。
代码如下:
Java语言:
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;import cn.itcast.xml.domain.Person;
/**
* 使用Dom解析xml文件
*
*/
public class DomXMLReader {public static List<Person> readXML(InputStream inStream) {
List<Person> persons = new ArrayList<Person>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document dom = builder.parse(inStream);
Element root = dom.getDocumentElement();
NodeList items = root.getElementsByTagName("person");//查找所有person节点
for (int i = 0; i < items.getLength(); i++) {
Person person = new Person();
//得到第一个person节点
Element personNode = (Element) items.item(i);
//获取person节点的id属性值
person.setId(new Integer(personNode.getAttribute("id")));
//获取person节点下的所有子节点(标签之间的空白节点和name/age元素)
NodeList childsNodes = personNode.getChildNodes();
for (int j = 0; j < childsNodes.getLength(); j++) {
Node node = (Node) childsNodes.item(j);
//判断是否为元素类型
if(node.getNodeType() == Node.ELEMENT_NODE){
Element childNode = (Element) node;
//判断是否name元素
if ("name".equals(childNode.getNodeName())) {
//获取name元素下Text节点,然后从Text节点获取数据 person.setName(childNode.getFirstChild().getNodeValue());
} else if (“age”.equals(childNode.getNodeName())) {
person.setAge(new Short(childNode.getFirstChild().getNodeValue()));
}
}
}
persons.add(person);
}
inStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return persons;
}
使用Pull解析器读取XML文件
除了可以使用 SAX和DOM解析XML文件,大家也可以使用Android内置的Pull解析器解析XML文件。 Pull解析器的运行方式与 SAX 解析器相似。它提供了类似的事件,如:开始元素和结束元素事件,使用parser.next()可以进入下一个元素并触发相应事件。事件将作为数值代码被发送,因此可以使用一个switch对感兴趣的事件进行处理。当元素开始解析时,调用parser.nextText()方法可以获取下一个Text类型节点的值。 使用Pull解析器读取itcast.xml的代码如下
Pull解析器的源码及文档下载网址:http://www.xmlpull.org/
Java语言:
import org.xmlpull.v1.XmlPullParser;
import android.util.Xml;
import cn.itcast.xml.domain.Person;public class PullXMLReader {public static List<Person> readXML(InputStreaminStream) {
XmlPullParser parser = Xml.newPullParser();
try {
parser.setInput(inStream, "UTF-8");
int eventType = parser.getEventType();
Person currentPerson = null;
List<Person> persons = null;
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 (name.equalsIgnoreCase("person")) {
currentPerson = new Person();
currentPerson.setId(new Integer(parser.getAttributeValue(null, "id")));
} else if (currentPerson != null) {
if (name.equalsIgnoreCase("name")) {
currentPerson.setName(parser.nextText());//
如果后面是Text节点,即返回它的值
} else if (name.equalsIgnoreCase("age")) {
currentPerson.setAge(new Short(parser.nextText()));
}
}
break;
case XmlPullParser.END_TAG://结束元素事件
if (parser.getName().equalsIgnoreCase("person") && currentPerson != null) {
persons.add(currentPerson);
currentPerson = null;
}
break;
}
eventType = parser.next();
}
inStream.close();
return persons;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
使用Pull解析器生成XML文件
有些时候,我们需要生成一个XML文件,生成XML文件的方法有很多,如:可以只使用一个StringBuilder组拼XML内容,然后把内容写入到文件中;或者使用DOM API生成XML文件,或者也可以使用pull解析器生成XML文件,这里推荐大家使用Pull解析器。 使用Pull解析器生成一个与itcast.xml文件内容相同的myitcast.xml文件,代码如下。
Java语言:
//生成XML文件:
File xmlFile = new File("myitcast.xml"); FileOutputStream outStream = new FileOutputStream(xmlFile);OutputStreamWriter outStreamWriter = new OutputStreamWriter(outStream, "UTF-8"); BufferedWriter writer =new BufferedWriter(outStreamWriter); writeXML(persons, writer); writer.flush(); writer.close();
//如果只想得到生成的xml字符串内容,可以使用StringWriter:
StringWriter writer = new StringWriter(); writeXML(persons, writer); String content = writer.toString();
Java语言:
public static String writeXML(List<Person> persons, Writer writer){
XmlSerializer serializer = Xml.newSerializer();
try {
serializer.setOutput(writer);
serializer.startDocument("UTF-8", true);
//第一个参数为命名空间,如果不使用命名空间,可以设置为null
serializer.startTag("", "persons");
for (Person person : persons){
serializer.startTag("", "person");
serializer.attribute("", "id", person.getId().toString());
serializer.startTag("", "name");
serializer.text(person.getName());
serializer.endTag("", "name");
serializer.startTag("", "age");
serializer.text(person.getAge().toString());
serializer.endTag("", "age");
serializer.endTag("", "person");
}
serializer.endTag("", "persons");
serializer.endDocument();
return writer.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
使用SharedPreferences进行数据存储
很多时候我们开发的软件需要向用户提供软件参数设置功能,例如我们常用的QQ,用户可以设置是否允许陌生人添加自己为好友。对于软件配置参数的保存,如果是window软件通常我们会采用ini文件进行保存,如果是j2se应用,我们会采用properties属性文件或者xml进行保存。如果是Android应用,我们最适合采用什么方式保存软件配置参数呢?Android平台给我们提供了一个SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数。使用SharedPreferences保存数据,其背后是用xml文件存放数据,文件存放在/data/data/<package
name>/shared_prefs目录下:
Java语言:
SharedPreferences sharedPreferences = getSharedPreferences("itcast", Context.MODE_PRIVATE);
Editor editor = sharedPreferences.edit();//获取编辑器
editor.putString("name", "传智播客");
editor.putInt("age", 4);
editor.commit();//提交修改
生成的itcast.xml文件内容如下:
XML语言:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="name">传智播客</string>
<int name="age" value="4" />
</map>
因为SharedPreferences背后是使用xml文件保存数据,getSharedPreferences(name,mode)方法的第一个参数用于指定该文件的名称,名称不用带后缀,后缀会由Android自动加上。方法的第二个参数指定文件的操作模式,共有四种操作模式,这四种模式前面介绍使用文件方式保存数据时已经讲解过。如果希望SharedPreferences背后使用的xml文件能被其他应用读和写,可以指定Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限。
另外Activity还提供了另一个getPreferences(mode)方法操作SharedPreferences,这个方法默认使用当前类不带包名的类名作为文件的名称。
访问SharedPreferences中的数据
Java语言:
//访问SharedPreferences中的数据代码如下:
SharedPreferences sharedPreferences = getSharedPreferences("itcast", Context.MODE_PRIVATE);
//getString()第二个参数为缺省值,如果preference中不存在该key,将返回缺省值
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", 1);//如果访问其他应用中的Preference,前提条件是:该preference创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE权限。
//如:有个<package name>为cn.itcast.action的应用使用下面语句创建了preference。
getSharedPreferences("itcast", Context.MODE_WORLD_READABLE);
//其他应用要访问上面应用的preference,首先需要创建上面应用的Context,然后通过Context 访问preference ,
//访问preference时会在应用所在包下的shared_prefs目录找到preference :
Context otherAppsContext = createPackageContext("cn.itcast.action", Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences sharedPreferences = otherAppsContext.getSharedPreferences("itcast",Context.MODE_WORLD_READABLE);
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", 0);
//如果不通过创建Context访问其他应用的preference,也可以以读取xml文件方式直接访问其他应用preference对应的xml文件,如:
File xmlFile = new File(“/data/data/<package name>/shared_prefs/itcast.xml”);//<package
name>应替换成应用的包名
相关文章推荐
- ANDROID基础学习笔记_4.1_采用pull解析xml文件
- Android笔记(四十六) Android中的数据存储——XML(二)PULL解析
- android 学习笔记 SAX 解析XML数据
- android学习记录2(日志、上下文、android下数据存储、xml基础)
- android菜鸟学习笔记19----Android数据存储(三)XML文件的解析及序列化
- Android数据存储---Pull解析Xml文件
- Android 个人学习笔记之---SAX解析XML文件
- Android数据存储---Pull解析Xml文件
- Jsoup学习笔记9:Jsoup 解析saz文件,读取其中的htm文件到字符串,提取字符串中的数据写入csv文件中
- Java基础知识强化之网络编程笔记18:Android网络通信之 使用HttpClient的Post / Get 方式读取网络数据(基于HTTP通信技术)
- android开发 解析服务器端xml文件数据存储到android客户端SQLite数据库
- 学习笔记之cocos2d-x2.1.1实现修改plist文件数据,用TinyXml解析XML
- Android入门笔记 - 数据存储 - 文件
- android 学习笔记 数据存储到文件中
- Python 数据分析(一) 本实验将学习 pandas 基础,数据加载、存储与文件格式,数据规整化,绘图和可视化的知识
- android 学习笔记 将数据存储到SharedPreferences中 并且读取数据
- 学习笔记之cocos2d-x2.1.1实现修改plist文件数据,用TinyXml解析XML
- Python 数据分析(一) 本实验将学习 pandas 基础,数据加载、存储与文件格式,数据规整化,绘图和可视化的知识
- Python学习入门基础教程(learning Python)--5.7 Python文件数据记录存储与处理
- XML 文件解析 -- MarsChen Android 开发教程学习笔记