zoukankan      html  css  js  c++  java
  • 在Android使用 SAX 方式进行异步解析

     

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

       

  • 相关阅读:
    Spring Boot 学习笔记(六)日志框架、日志配置
    Spring Boot 学习笔记(五)多配置文件、Profile参数、外部配置、@EnableAutoConfiguration、@Conditional
    Spring Boot 学习笔记(四)@PropertySource、@ImportResource、@Bean
    Spring Boot 学习笔记(三)配置文件、yaml、@ConfigurationProperties
    Spring Boot 学习笔记(二)使用 spring initializer 快速构建 boot 项目
    Spring Boot 学习笔记(一)基础依赖配置、@SpringBootApplication 注解、官方文档
    HDU 5936 Difference ( 2016 CCPC 杭州 D && 折半枚举 )
    BZOJ 3926 诸神眷顾的幻想乡 ( 广义后缀自动机 )
    git——解决每次拉取、提交代码时都需要输入用户名和密码
    Vue——解决使用第三方组件库时无法修改默认样式的问题(使用 /deep/ )
  • 原文地址:https://www.cnblogs.com/encounter/p/2188498.html
Copyright © 2011-2022 走看看