在Android使用 SAX 方式进行异步解析
2011-04-20 14:37
176 查看
对XML文件进行解析有两种:DOM方式和SAX方式。在Android应用中,多采用SAX方式。这种方式是基于方法回调的,解析速度快,内存消耗小。我们在SAX解析中增加了异步更新主线程的代码,使用户体验更佳。
一、主线程
主线程是Activity,实现了自定义接口AsyncSaxResponse。该接口定义了3个方法,分别是:
public
void
parserUpdateNotify(ArrayList<Map<String,Object>> al);
public
void
parserStartNotify();
public
void
parserEndNotify(ArrayList<Map<String,Object>>
al);
为简单起见,我们使用了ListActivity,这样不需要写xml。整个Activity的代码如下:
public
class
main
extends
ListActivity
implements
AsyncSaxResponse{
/** Called when the activity is first created. */
@Override
public
void
onCreate(Bundle
savedInstanceState) {
super
.onCreate(savedInstanceState);
//
登录服务器,并取得结果
String url =
this
.getString(R.string.
URL
);
ColDetailParser handler =
new
ColDetailParser(
this
);
AsyncSaxHelper.execute
(url, handler);
}
public
void
parserUpdateNotify(ArrayList<Map<String,Object>> al){
//
Log.i("maps",""+al
);
//
构建
listView
的适配器
SimpleAdapter adapter=
new
SimpleAdapter(
this
,al,
android.R.layout.
simple_list_item_2
,
// SDK
库中提供的一个包含两个
TextView
的
layout
new
String[]{
"rowname"
,
"data"
},
// maps
中的两个
key
new
int
[]{android.R.id.
text1
,android.R.id.
text2
}
//
两个
TextView
的
id
);
this
.setListAdapter(adapter);
//
adapter.notifyDataSetChanged();
}
@Override
public
void
parserStartNotify() {
//
TODO
Auto-generated
method stub
}
@Override
public
void
parserEndNotify(ArrayList<Map<String, Object>> al) {
//
TODO
Auto-generated
method stub
}
}
在onCreate方法中,我们使用了AsyncSaxHelper进行xml文件的解析,其解析器是ColDetailParser(继承了DefaultHandler),这两个类后面介绍。
需要注意的是,ColDetailParser的构造函数需要一个AsyncSaxResponse类的参数。我们把main.this传递给它,这样main就必须实现AsyncSaxResponse的3个接口方法了。
在
parserUpdateNotify
方法中,我们对主线程UI进行刷新。这个方法在SAX解析过程中会调用多次,比如每解析到一个节点就调用一次(具体看ColDetailParser类的代码)。这样,每当解析到新的节点,listview会重新绑定,从而产生列表被渐次刷新的效果。
另外两个接口方法一个是在开始解析xml文档时调用,另一个是在xml文档解析结束时调用,我们都采用了空实现。你可以分别在两个方法中加入自己的代码,比如文档开始解析锁住某些UI组件,一直到解析完成。
二、AsyncSaxHelper类
AsyncSaxHelper类只有一个方法execute,用于构造URL请求,然后调用Sax解析器进行解析。
public
class
AsyncSaxHelper {
private
static
String
tag
=
"AsyncSaxHelper"
;
public
static
void
execute(String
url,
DefaultHandler handler) {
//
该方法通过
url
获得
xml
并解析
xml
内容为
Dept
对象数组
try
//
异常处理
{
//
url
=
Utils.getInstance().UrlWrap(url
);
Log.w
(
"getObjects url:"
,url);
URL _url =
new
URL(url);
//
构建
Sax
解析器工厂
SAXParserFactory factory =
SAXParserFactory.newInstance
();
//
使用
Sax
解析器工厂构建
Sax
解析器
SAXParser parser =
factory.newSAXParser();
//
使用
Sax
解析器构建
xml
Reader
XMLReader xmlreader =
parser.getXMLReader();
//
绑定
handler
到
xml
reader
xmlreader.setContentHandler(handler);
//
对于中文编码,要进行如下处理==========
Log.w
(
"open stream"
,
""
+
new
java.util.Date().getTime());
InputStream stream =
_url.openStream();
InputStreamReader sr =
new
InputStreamReader(stream,
"gb2312"
);
InputSource is =
new
InputSource(sr);
//
========================
Log.w
(
"xml parse bengin"
,
""
+
new
java.util.Date().getTime());
//
读取
xml
,进行
sax
解析
xmlreader.parse(is);
// Log.i(tag,handler.getObjects().toString());
//
将解析结果返回
//
return handler.getObjects();
}
catch
(Exception ee) {
ee.printStackTrace();
//
return null;
}
}
}
你可以进一步改进这个方法,将其封装为线程方法。
三、SAX解析器
真正的解析动作由ColDetailParser完成,ColDetailParser继承了DefaultHandler并实现其5个方法:
public
class
ColDetailParser
extends
DefaultHandler {
//
继承
DefaultHandler,
方便进行
sax
//
解析
final
static
String
tag
=
"ColDetailParser"
;
Map<String, Object>
_map
;
//
临时变量,用于保存解析过程中的元素值
ArrayList<Map<String,
Object>>
_maps
;
int
flag
= 0;
boolean
isTable
=
false
;
private
AsyncSaxResponse
delegate
;
ColDetailParser(AsyncSaxResponse
delegate) {
this
.
delegate
=delegate;
}
//
下面通过重载
DefaultHandler
的
5
个方法来实现
sax
解析
public
void
startDocument()
throws
SAXException {
//
这个方法在解析
xml
文档的一开始执行
,
一般我们需要在该方法中初始化解析过程中有可能用到的变量
Log.w
(
"start
document"
,
""
+
new
java.util.Date().getTime());
_maps
=
new
ArrayList<Map<String,Object>>();
if
(
delegate
!=
null
){
try
{
//
获取
delegate
的
parserUpdateNotify
并调用
,
该方法有一个
ArrayList
参数
Method m =
delegate
.getClass().getMethod(
"parserStartNotify"
);
if
(m !=
null
)
m.invoke(
delegate
);
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
public
void
endDocument()
throws
SAXException {
//
这个方法在整个
xml
文档解析结束时执行
,
一般需要在该方法中返回或保存整个文档解析解析结果
,
但由于
//
我们已经在解析过程中把结果保持在全局变量中
,
所以这里什么也不做
Log.w
(
"end
document"
,
""
+
new
java.util.Date().getTime());
if
(
delegate
!=
null
){
try
{
//
获取
delegate
的
parserUpdateNotify
并调用
,
该方法有一个
ArrayList
参数
Method m =
delegate
.getClass().getMethod(
"parserEndNotify"
,
new
Class[]{ArrayList.
class
});
if
(m !=
null
)
m.invoke(
delegate
,
_maps
);
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
public
void
startElement(String namespaceURI, String localName,
String qName, Attributes atts)
throws
SAXException {
//
这个方法在解析标签开始标记时执行
,
我们关心
2
个元素,在这里判断这
2
个元素并做上对应标记
if
(localName.equals(
"table"
)) {
//
若是
table
标签
,
初始化
_map
_map
=
new
HashMap<String,
Object>();
isTable
=
true
;
return
;
}
if
(
isTable
&&localName.equals(
"row_id"
)) {
//
若是
row_id
标签
,
置
flag
标志为
1,
flag
= 1;
return
;
}
if
(
isTable
&&localName.equals(
"name"
)) {
//
若是
name
标签
,
置
flag
标志为
2,
flag
= 2;
return
;
}
if
(
isTable
&&localName.equals(
"data"
)) {
//
若是
data
标签
,
置
flag
标志为
3,
flag
= 3;
return
;
}
}
public
void
characters(
char
ch[],
int
start,
int
length) {
//
这个方法在解析标签内容
(
即开始标记-结束标记之间的部分
)
时执行
,
一般我们在里这获取元素体内容
,
在本例中,我们只关心
3
个元素体
String theString =
new
String(ch, start, length);
//
获取元素体内容
switch
(
flag
) {
case
1:
if
(
_map
==
null
)Log.i
(
tag
,
"_map is null"
);
_map
.put(
"rowid"
, theString);
break
;
case
2:
_map
.put(
"rowname"
,
theString);
break
;
case
3:
_map
.put(
"data"
, theString);
default
:
break
;
}
flag
= 0;
//
标记归零
}
public
void
endElement(String
namespaceURI, String localName, String qName)
throws
SAXException {
//
这个方法在解析标签结束标记时执行
,
一般我们需要在该方法保存元素内容
if
(localName.equals(
"table"
)) {
// table
标签结束
,
把
_map
保存到
_maps
数组
Log.w
(
"table element"
,
""
+
new
java.util.Date().getTime());
_maps
.add(
_map
);
isTable
=
false
;
flag
=0;
if
(
delegate
!=
null
){
try
{
//
获取
delegate
的
parserUpdateNotify
并调用
,
该方法有一个
ArrayList
参数
Method m =
delegate
.getClass().getMethod(
"parserUpdateNotify"
,
new
Class[]{ArrayList.
class
});
if
(m !=
null
)
m.invoke(
delegate
,
_maps
);
}
catch
(Exception e) {
e.printStackTrace();
}
}
return
;
}
}
}
真正由意思的事情发生在构造方法里。我们传递了一个AsyncSaxResponse接口对象给构造方法,并将其保存到实例变量delegate里。这样在适当的时候(比如在startDocument、endDocument和endElement里)就可以通过delegate对象回调主线程的3个接口方法了。通过这种方式,实现了异步解析中同步更新主线程的UI。
运行程序,模拟器界面和Logcat输出信息如下:
一、主线程
主线程是Activity,实现了自定义接口AsyncSaxResponse。该接口定义了3个方法,分别是:
public
void
parserUpdateNotify(ArrayList<Map<String,Object>> al);
public
void
parserStartNotify();
public
void
parserEndNotify(ArrayList<Map<String,Object>>
al);
为简单起见,我们使用了ListActivity,这样不需要写xml。整个Activity的代码如下:
public
class
main
extends
ListActivity
implements
AsyncSaxResponse{
/** Called when the activity is first created. */
@Override
public
void
onCreate(Bundle
savedInstanceState) {
super
.onCreate(savedInstanceState);
//
登录服务器,并取得结果
String url =
this
.getString(R.string.
URL
);
ColDetailParser handler =
new
ColDetailParser(
this
);
AsyncSaxHelper.execute
(url, handler);
}
public
void
parserUpdateNotify(ArrayList<Map<String,Object>> al){
//
Log.i("maps",""+al
);
//
构建
listView
的适配器
SimpleAdapter adapter=
new
SimpleAdapter(
this
,al,
android.R.layout.
simple_list_item_2
,
// SDK
库中提供的一个包含两个
TextView
的
layout
new
String[]{
"rowname"
,
"data"
},
// maps
中的两个
key
new
int
[]{android.R.id.
text1
,android.R.id.
text2
}
//
两个
TextView
的
id
);
this
.setListAdapter(adapter);
//
adapter.notifyDataSetChanged();
}
@Override
public
void
parserStartNotify() {
//
TODO
Auto-generated
method stub
}
@Override
public
void
parserEndNotify(ArrayList<Map<String, Object>> al) {
//
TODO
Auto-generated
method stub
}
}
在onCreate方法中,我们使用了AsyncSaxHelper进行xml文件的解析,其解析器是ColDetailParser(继承了DefaultHandler),这两个类后面介绍。
需要注意的是,ColDetailParser的构造函数需要一个AsyncSaxResponse类的参数。我们把main.this传递给它,这样main就必须实现AsyncSaxResponse的3个接口方法了。
在
parserUpdateNotify
方法中,我们对主线程UI进行刷新。这个方法在SAX解析过程中会调用多次,比如每解析到一个节点就调用一次(具体看ColDetailParser类的代码)。这样,每当解析到新的节点,listview会重新绑定,从而产生列表被渐次刷新的效果。
另外两个接口方法一个是在开始解析xml文档时调用,另一个是在xml文档解析结束时调用,我们都采用了空实现。你可以分别在两个方法中加入自己的代码,比如文档开始解析锁住某些UI组件,一直到解析完成。
二、AsyncSaxHelper类
AsyncSaxHelper类只有一个方法execute,用于构造URL请求,然后调用Sax解析器进行解析。
public
class
AsyncSaxHelper {
private
static
String
tag
=
"AsyncSaxHelper"
;
public
static
void
execute(String
url,
DefaultHandler handler) {
//
该方法通过
url
获得
xml
并解析
xml
内容为
Dept
对象数组
try
//
异常处理
{
//
url
=
Utils.getInstance().UrlWrap(url
);
Log.w
(
"getObjects url:"
,url);
URL _url =
new
URL(url);
//
构建
Sax
解析器工厂
SAXParserFactory factory =
SAXParserFactory.newInstance
();
//
使用
Sax
解析器工厂构建
Sax
解析器
SAXParser parser =
factory.newSAXParser();
//
使用
Sax
解析器构建
xml
Reader
XMLReader xmlreader =
parser.getXMLReader();
//
绑定
handler
到
xml
reader
xmlreader.setContentHandler(handler);
//
对于中文编码,要进行如下处理==========
Log.w
(
"open stream"
,
""
+
new
java.util.Date().getTime());
InputStream stream =
_url.openStream();
InputStreamReader sr =
new
InputStreamReader(stream,
"gb2312"
);
InputSource is =
new
InputSource(sr);
//
========================
Log.w
(
"xml parse bengin"
,
""
+
new
java.util.Date().getTime());
//
读取
xml
,进行
sax
解析
xmlreader.parse(is);
// Log.i(tag,handler.getObjects().toString());
//
将解析结果返回
//
return handler.getObjects();
}
catch
(Exception ee) {
ee.printStackTrace();
//
return null;
}
}
}
你可以进一步改进这个方法,将其封装为线程方法。
三、SAX解析器
真正的解析动作由ColDetailParser完成,ColDetailParser继承了DefaultHandler并实现其5个方法:
public
class
ColDetailParser
extends
DefaultHandler {
//
继承
DefaultHandler,
方便进行
sax
//
解析
final
static
String
tag
=
"ColDetailParser"
;
Map<String, Object>
_map
;
//
临时变量,用于保存解析过程中的元素值
ArrayList<Map<String,
Object>>
_maps
;
int
flag
= 0;
boolean
isTable
=
false
;
private
AsyncSaxResponse
delegate
;
ColDetailParser(AsyncSaxResponse
delegate) {
this
.
delegate
=delegate;
}
//
下面通过重载
DefaultHandler
的
5
个方法来实现
sax
解析
public
void
startDocument()
throws
SAXException {
//
这个方法在解析
xml
文档的一开始执行
,
一般我们需要在该方法中初始化解析过程中有可能用到的变量
Log.w
(
"start
document"
,
""
+
new
java.util.Date().getTime());
_maps
=
new
ArrayList<Map<String,Object>>();
if
(
delegate
!=
null
){
try
{
//
获取
delegate
的
parserUpdateNotify
并调用
,
该方法有一个
ArrayList
参数
Method m =
delegate
.getClass().getMethod(
"parserStartNotify"
);
if
(m !=
null
)
m.invoke(
delegate
);
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
public
void
endDocument()
throws
SAXException {
//
这个方法在整个
xml
文档解析结束时执行
,
一般需要在该方法中返回或保存整个文档解析解析结果
,
但由于
//
我们已经在解析过程中把结果保持在全局变量中
,
所以这里什么也不做
Log.w
(
"end
document"
,
""
+
new
java.util.Date().getTime());
if
(
delegate
!=
null
){
try
{
//
获取
delegate
的
parserUpdateNotify
并调用
,
该方法有一个
ArrayList
参数
Method m =
delegate
.getClass().getMethod(
"parserEndNotify"
,
new
Class[]{ArrayList.
class
});
if
(m !=
null
)
m.invoke(
delegate
,
_maps
);
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
public
void
startElement(String namespaceURI, String localName,
String qName, Attributes atts)
throws
SAXException {
//
这个方法在解析标签开始标记时执行
,
我们关心
2
个元素,在这里判断这
2
个元素并做上对应标记
if
(localName.equals(
"table"
)) {
//
若是
table
标签
,
初始化
_map
_map
=
new
HashMap<String,
Object>();
isTable
=
true
;
return
;
}
if
(
isTable
&&localName.equals(
"row_id"
)) {
//
若是
row_id
标签
,
置
flag
标志为
1,
flag
= 1;
return
;
}
if
(
isTable
&&localName.equals(
"name"
)) {
//
若是
name
标签
,
置
flag
标志为
2,
flag
= 2;
return
;
}
if
(
isTable
&&localName.equals(
"data"
)) {
//
若是
data
标签
,
置
flag
标志为
3,
flag
= 3;
return
;
}
}
public
void
characters(
char
ch[],
int
start,
int
length) {
//
这个方法在解析标签内容
(
即开始标记-结束标记之间的部分
)
时执行
,
一般我们在里这获取元素体内容
,
在本例中,我们只关心
3
个元素体
String theString =
new
String(ch, start, length);
//
获取元素体内容
switch
(
flag
) {
case
1:
if
(
_map
==
null
)Log.i
(
tag
,
"_map is null"
);
_map
.put(
"rowid"
, theString);
break
;
case
2:
_map
.put(
"rowname"
,
theString);
break
;
case
3:
_map
.put(
"data"
, theString);
default
:
break
;
}
flag
= 0;
//
标记归零
}
public
void
endElement(String
namespaceURI, String localName, String qName)
throws
SAXException {
//
这个方法在解析标签结束标记时执行
,
一般我们需要在该方法保存元素内容
if
(localName.equals(
"table"
)) {
// table
标签结束
,
把
_map
保存到
_maps
数组
Log.w
(
"table element"
,
""
+
new
java.util.Date().getTime());
_maps
.add(
_map
);
isTable
=
false
;
flag
=0;
if
(
delegate
!=
null
){
try
{
//
获取
delegate
的
parserUpdateNotify
并调用
,
该方法有一个
ArrayList
参数
Method m =
delegate
.getClass().getMethod(
"parserUpdateNotify"
,
new
Class[]{ArrayList.
class
});
if
(m !=
null
)
m.invoke(
delegate
,
_maps
);
}
catch
(Exception e) {
e.printStackTrace();
}
}
return
;
}
}
}
真正由意思的事情发生在构造方法里。我们传递了一个AsyncSaxResponse接口对象给构造方法,并将其保存到实例变量delegate里。这样在适当的时候(比如在startDocument、endDocument和endElement里)就可以通过delegate对象回调主线程的3个接口方法了。通过这种方式,实现了异步解析中同步更新主线程的UI。
运行程序,模拟器界面和Logcat输出信息如下:
相关文章推荐
- 在Android使用 SAX 方式进行异步解析
- Android 使用Dom与SAX解析xml文档的方式
- Android中使用SAX对XMl文件进行解析
- Android异步下载网络图片&android解析xml文件的方式&使用Adapter为ListView提供数据
- Android中使用SAX方式解析XML文件
- Android中使用SAX对XMl文件进行解析
- 使用SAX方式对XML进行解析
- Android中使用SAX方式解析XML文件
- Android异步下载网络图片&android解析xml文件的方式&使用Adapter为ListView提供数据
- Android中使用SAX方式解析XML文件
- Android中使用PULL方式解析XML和XmlSerializer进行序列化
- Android中使用SAX方式解析XML文件
- Android学习指南之三十一:Android中使用SAX和pull方式解析XML
- 几种常见的xml解析方式 SAX,DOM,PULL以android为例
- Android应用中使用DOM方式解析XML格式数据的基本方法
- Android中使用Pull解析器解析xml文件+进行简单的单元测试
- SAX方式进行XML解析
- 一个android中使用sax解析xml的例子
- Android中使用Volley开源库进行Http网络请求(GET方式)
- android中使用哪种方式解析XML比较好