安卓解析XML并显示在ListView

好久不碰XML解析,在此写了个小的demo来温习一下解析XML,其中我用的sax来解析XML,XML文件存放在tomcat服务器,由安卓端通过http的GET请求获取到xml,之后就是解析啦,解析完将所有数据存放在实体类中,接下来就是将数据显示在ListView上面了。demo虽小,但其中遇到一些问题卡住我半天,下面就来看看吧。服务器的搭建比较简单了,其中在servlet中处理安卓端的get请求,将book.xml以字节流的方式返回给安卓端</span>
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Properties;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class HTTPServlet extends HttpServlet {
	String data;
	/**
	 * Constructor of the object.
	 */
	public HTTPServlet() {
		super();
	}

	/**
	 * Destruction of the servlet. <br>
	 */
	public void destroy() {
		super.destroy(); // Just puts "destroy" string in log
		// Put your code here
	}

	/**
	 * The doGet method of the servlet. <br>
	 * 
	 * This method is called when a form has its tag value method equals to get.
	 * 
	 * @param request
	 *            the request send by the client to the server
	 * @param response
	 *            the response send by the server to the client
	 * @throws ServletException
	 *             if an error occurred
	 * @throws IOException
	 *             if an error occurred
	 */
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		response.setContentType("text");
		response.setCharacterEncoding("UTF-8");
		PrintWriter out = response.getWriter();
		String name=request.getParameter("name");
		String password=request.getParameter("password");
		InputStream inputStream=HTTPServlet.class.getClassLoader().getResourceAsStream("books.xml");
		if(inputStream!=null){
			
			StringBuilder sb=new StringBuilder();
			BufferedReader reader=new BufferedReader(new InputStreamReader(inputStream,"utf-8"));
			
			while((data=reader.readLine())!=null){
				sb.append(data);
			}
			out.print(sb.toString());
			System.out.println(sb.toString());
		}else {
			out.print("input  null");
		}
	}

	/**
	 * The doPost method of the servlet. <br>
	 * 
	 * This method is called when a form has its tag value method equals to
	 * post.
	 * 
	 * @param request
	 *            the request send by the client to the server
	 * @param response
	 *            the response send by the server to the client
	 * @throws ServletException
	 *             if an error occurred
	 * @throws IOException
	 *             if an error occurred
	 */
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

	/**
	 * Initialization of the servlet. <br>
	 * 
	 * @throws ServletException
	 *             if an error occurs
	 */
	public void init() throws ServletException {
		// Put your code here
	}

}



再回到安卓端,安卓端主要处理的就是xml文件的解析,下面一步步说下所有流程

点击打开链接

首先,看看xml文件

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
	<book id="1">
		<name>冰与火之歌</name>
		<author>乔治马丁</author>
		<year>2014</year>
		<price>89</price>
	</book>
	<book id="2">
		<name>安徒生童话</name>
		<year>2004</year>
		<price>77</price>
		<language>English</language>
	</book>
</bookstore>



我们创建与之对应的实体类Book

import java.io.Serializable;

public class Book implements Serializable {

	private static final long serialVersionUID = 1L;
	private String id;
	private String name;
	private String price;
	private String author;
	private String year;
	private String language;

	public String getLanguage() {
		return language;
	}

	public void setLanguage(String language) {
		this.language = language;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPrice() {
		return price;
	}

	public void setPrice(String price) {
		this.price = price;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public String getYear() {
		return year;
	}

	public void setYear(String year) {
		this.year = year;
	}

	public static long getSerialversionuid() {
		return serialVersionUID;
	}

	@Override
	public String toString() {
		return "Book [id=" + id + ", name=" + name + ", price=" + price
				+ ", author=" + author + ", year=" + year + ", language="
				+ language + "]";
	}

}



所有属性的set,get方法自行填补


以下的ParseXML类主要负责通过HTTP协议获取服务器返回数据流


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import android.R.xml;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.ListView;
import android.widget.TextView;

public class ParseXML extends Thread {
	public static final int SHOW_LISTVIEW=0x11;
	private String path;
	private Handler handler;
	private List<Book> books;
	public ParseXML(String path) {
		this.path=path;
	}

	public ParseXML(String path, Handler handler) {
		this.path = path;
		this.handler = handler;
	}

	@Override
	public void run() {
		super.run();
		try {
			URL url = new URL(path);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setConnectTimeout(5000);
			conn.setRequestMethod("GET");
			conn.setReadTimeout(5000);
			//获取服务器返回的输入流
			String date = getXMLInputStream(conn.getInputStream());
			
			SAXParserFactory factory=SAXParserFactory.newInstance();
			XMLReader xmlreader=factory.newSAXParser().getXMLReader();
			ParseXMLHandler parse=new ParseXMLHandler();
			xmlreader.setContentHandler(parse);
			xmlreader.parse(new InputSource(new StringReader(date.toString())));
			books= parse.getAllBooks();
			Log.i("book_lenth", books.size()+"");
			Log.i("books", books.toString()+"");
			
			Message message=Message.obtain();
			message.what=SHOW_LISTVIEW;
			message.obj=books;
			handler.sendMessage(message);
			
			conn.disconnect();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}


	public String getXMLInputStream(InputStream inputStream) {
		BufferedReader reader;
		StringBuilder sb = new StringBuilder();
		String str;
		try {
			reader = new BufferedReader(new InputStreamReader(inputStream,
					"utf-8"));
			if (reader != null) {
				while ((str = reader.readLine()) != null) {
					sb.append(str);
				}
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return sb.toString();
	}
	
	public List<Book> getBooks(){
		return books;
	}
	
}

ParseXMLHandler类主要负责用SAX方式解析xml文件

 SAX与DOM比较而言,SAX是一种轻量型的方法。我们知道,在处理DOM的时候,我们需要读入整个的XML文档,然后在内存中创建DOM树,生成DOM树上的每个Node对象。当文档比较小的时候,这不会造成什么问题,但是一旦文档大起来,处理DOM就会变得相当费时费力。特别是其对于内存的需求,也将是成倍的增长,以至于在某些应用中使用DOM是一件很不划算的事(比如在applet中)。这时候,一个较好的替代解决方法就是SAX。

     SAX在概念上与DOM完全不同。它不同于DOM的文档驱动,它是事件驱动的,它并不需要读入整个文档,而文档的读入过程也就是SAX的解析过程。所谓事件驱动,是指一种基于回调(callback)机制的程序运行方法。


startDocument()和endDocument()在开始和结束解析xml文件时调用,可用于家在一些配置

startElement()和endElement()在解析开始元素和结束元素时候调用

characters()是对元素的值的获取,其中不要忽略xml文件空白处此方法也会被调用,但获取的值均为空

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 android.util.Log;

public class ParseXMLHandler extends DefaultHandler {
	private  List<Book> books;
	private Book book;
	private String qname;
	String data = null;

	public ParseXMLHandler() {
		books = new ArrayList<Book>();
	}

	@Override
	public void characters(char[] ch, int start, int length)
			throws SAXException {
		super.characters(ch, start, length);
		if(qname!=null){
			data = new String(ch, start, length);
			if ("name".equals(qname)) {
				book.setName(data);
			} else if ("author".equals(qname)) {
				book.setAuthor(data);
			} else if ("price".equals(qname)) {
				book.setPrice(data);
			} else if ("year".equals(qname)) {
				book.setYear(data);
			} else if ("language".equals(qname)) {
				book.setLanguage(data);
			}

		}

	}

	@Override
	public void endDocument() throws SAXException {
		super.endDocument();
//		Log.i("TAG", "stop parse xml");
	}

	@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		super.endElement(uri, localName, qName);
		
		//此处用qName而不用qname!!!!!!!!!
		if ("book".equals(qName)) {
			books.add(book);
			book = null;
		}
		qname=null;
		
		
	}

	@Override
	public void startDocument() throws SAXException {
		super.startDocument();
//		Log.i("TAG", "start parse xml");
	}

	@Override
	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
		super.startElement(uri, localName, qName, attributes);
		if (qName.equals("book")) {
			book = new Book();
			int num = attributes.getLength();
			// 遍历所有的属性
			for (int i = 0; i < num; i++) {
				String name = attributes.getQName(i);
				String value = attributes.getValue(i);
				if ("id".equals(name)) {
					book.setId(value);
				}
			}
		} 
		this.qname = qName;
	}

	public  List<Book> getAllBooks() {
		return books;
	}

}

接下来就是MainActivity的简单说明,MainActivity主要用于显示xml解析出来的数据,其中封装在集合中的数据要通过Handler来发送给主线程,让主线程来更新UI,因为只有主线程才可以更新UI,子线程更新UI是不安全的。


import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity implements OnClickListener {
	private Button bt_get;
	private ListView listView;
	private BookAdapter adapter;
	private List<Book> books;
	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case ParseXML.SHOW_LISTVIEW:
				books = (List<Book>) msg.obj;
				 adapter = new BookAdapter(MainActivity.this, books);
				 listView.setAdapter(adapter);
				break;

			default:
				break;
			}
		};
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		bt_get = (Button) findViewById(R.id.bt_get);
		listView = (ListView) findViewById(R.id.listView);
		bt_get.setOnClickListener(this);

	}

	@Override
	public void onClick(View v) {
		ParseXML parseXML = new ParseXML(
				"http://192.168.1.118:8080/androidService/servlet/HTTPServlet",
				handler);
		parseXML.start();
		books = parseXML.getBooks();

		// 不能在此处更新UI,其中问题在于,ParseXML类去解析服务器返回的流是在子线程中进行,
		// 直接在此处给adapter设置books,会获取不到books的集合,简单做法就是,子线程将数据通过handler发送给主线程来更新UI
		// if(books!=null){
		// adapter = new BookAdapter(this, books);
		// listView.setAdapter(adapter);
		// }else {
		// Log.i("TAG", "books is null");
		// }
	}
}


最后adapter和布局文件就不贴上来了,比较简单。


demo下载地址点击打开链接

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值