對 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輸出信息如下:
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
