您的位置:首页 > 移动开发 > Android开发

在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输出信息如下:





内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: