Giới thiệu

Android là một hệ điều hành nguồn mở, hiện đại và là SDK cho các thiết bị di động. Với hệ điều hành này, bạn có thể tạo ra các ứng dụng di động rất mạnh. Điều này thậm chí còn trở nên hấp dẫn hơn nữa khi các ứng dụng của bạn có thể truy cập các dịch vụ Web, có nghĩa là bạn cần sử dụng ngôn ngữ của Web là: XML. Trong bài viết này, bạn sẽ thấy nhiều lựa chọn khác nhau để làm việc với XML trên Android và cách sử dụng chúng để xây dựng các ứng dụng Android của chính bạn.

Bắt đầu

Trong bài viết này, bạn học cách xây dựng các ứng dụng Android có thể làm việc với XML từ Internet. Các ứng dụng Android được viết bằng ngôn ngữ lập trình Java™, do vậy mà kinh nghiệm làm việc với công nghệ Java là điều cần phải có. Để phát triển cho Android, bạn sẽ cần đến Android SDK. Toàn bộ mã trình được trình bày trong bài viết này sẽ làm việc với bất kỳ phiên bản nào của Android SDK, nhưng phiên bản SDK 1.5_pre đã được sử dụng để phát triển mã trình. Bạn có thể phát triển các ứng dụng Android chỉ với SDK và một trình biên tập văn bản là đủ, nhưng sẽ dễ dàng hơn nhiều nếu sử dụng Android Developer Tools (ADT), là một trình bổ sung Eclipse. Đối với bài viết này, phiên bản 0.9 của ADT đã được dùng với Eclipse 3.4.2, một phiên bản Java.

XML trên Android

Nền tảng Android là một nền tảng phát triển di động mã nguồn mở. Nó giúp bạn truy cập vào tất cả các khía cạnh của thiết bị di động mà nó chạy trên đó, từ các đồ họa cấp thấp, đến phần cứng như là thiết bị camera trên điện thoại. Với rất nhiều thứ có thể sử dụng Android, có thể bạn sẽ tự hỏi tại sao bạn cần phiền đến XML. Đó không phải vì làm việc với XML rất thú vị; mà là nó đang làm việc với những thứ mà nó kích hoạt. XML thường được dùng như là một định dạng dữ liệu trên Internet. Nếu bạn muốn truy cập dữ liệu từ Internet, các khả năng có thể là dữ liệu sẽ ở dạng XML. Nếu bạn muốn gửi dữ liệu đến một dịch vụ Web, có thể bạn cũng cần gửi cả dữ liệu XML. Nói ngắn gọn là nếu ứng dụng Android của bạn thúc đẩy Internet, thì có thể bạn sẽ cần phải làm việc với XML. Thật may mắn là bạn có rất nhiều lựa chọn có sẵn để làm việc với XML trên Android.

Các trình phân tích XML

Một trong nhữn ưu điểm lớn nhất của nền tảng Android chính là việc nó thúc đẩy ngôn ngữ lập trình Java. Android SDK không hoàn toàn cung cấp sẵn mọi thứ cho Môi trường Thời gian chạy Java (JRE) chuẩn của bạn, nhưng nó lại hỗ trợ một phần rất đáng kể cho nó. Nền tảng Java đã và đang hỗ trợ rất nhiều cách khác nhau để làm việc với XML trong thời gian nhất định, và hầu hết các API có liên quan đến XML của Java đều được hỗ trợ đầy đủ trên Android. Ví dụ, Simple API của Java cho XML (SAX) và Document Object Model (DOM) hiện đều có sẵn trên Android. Nhiều năm qua, cả hai API này là một phần của công nghệ Java. Sản phẩm Streaming API mới đây cho XML (StAX) hiện chưa có trong Android. Tuy nhiên, Android lại cung cấp một thư viện tương đương về mặt chức năng. Điều cuối cùng là Java XML Binding API cũng không có sẵn trong Android. Chắc chắn có thể thực hiện API này trong Android. Tuy nhiên, nó lại có xu hướng là một API nặng ký, với rất nhiều thể hiện khác nhau thuộc các lớp khác nhau thường cần việc trình bày một tài liệu XML. Do vậy mà nó không lý tưởng lắm cho một môi trường bị ràng buộc chẳng hạn như thiết bị cầm tay mà Android được thiết kế để chạy trên đó. Trong các phần tiếp theo, bạn sẽ lấy một nguồn XML đơn giản có sẵn trên Internet, và xem cách phân tích nguồn đó như thế nào trong phạm vi một ứng dụng Android sử dụng các API khác nhau được nhắc đến ở trên.Trước tiên, hãy xem các phần cần thiết của ứng dụng đơn giản sẽ sử dụng XML từ Internet.

Các từ viết tắt thông dụng

  • API: Application programming interface (Giao diện lập trình ứng dụng)
  • RSS: Really Simple Syndication (Giao thức tập hợp thông tin đơn giản)
  • SDK: Software Developers Kit (Bộ dụng cụ cho nhà phát triển phầm mềm)
  • UI: User interface (Giao diện người dùng)
  • URL: Universal Resource Locator (Địa chỉ tài nguyên)
  • XML: Extensible Markup Language (Ngôn ngữ đánh dấu mở rộng)

Trình đọc tin Android

Ứng dụng sẽ lấy điểm tin RSS từ trang nhà phát triển Android phổ biến Androidster và phân tách nó thành một danh sách các đối tượng Java đơn giản mà bạn có thể sử dụng để quay lại Android ListView. Đây là hoạt động đa hình thái cổ điển — tức là các thực thi khác nhau (các thuật toán phân tích XML khác nhau) cung cấp hoạt động giống nhau. Ví dụ 1 dưới đây cho bạn thấy bạn có thể mô hình hóa điều này dễ dàng như thế nào trong mã trình Java sử dụng một giao diện.
Ví dụ 1. giao diện trình phân tích điểm tin XML

package org.developerworks.android;

import java.util.List;

public interface FeedParser {

List<Message> parse();

}

Trong ví dụ 2, lớp Message là một POJO (Plain Old Java Object) cổ điển miêu tả một cấu trúc dữ liệu.
Ví dụ 2. Message POJO

public class Message implements Comparable<Message>{

static SimpleDateFormat FORMATTER =

new SimpleDateFormat(“EEE, dd MMM yyyy HH:mm:ss Z”);

private String title;

private URL link;

private String description;

private Date date;

// getters and setters omitted for brevity

public void setLink(String link) {

try {

this.link = new URL(link);

} catch (MalformedURLException e) {

throw new RuntimeException(e);

}

}

public String getDate() {

return FORMATTER.format(this.date);

}

public void setDate(String date) {

// pad the date if necessary

while (!date.endsWith(“00”)){

date += “0”;

}

try {

this.date = FORMATTER.parse(date.trim());

} catch (ParseException e) {

throw new RuntimeException(e);

}

}

@Override

public String toString() {

// omitted for brevity

}

@Override

public int hashCode() {

// omitted for brevity

}

@Override

public boolean equals(Object obj) {

// omitted for brevity

}

// sort by date

public int compareTo(Message another) {

if (another == null) return 1;

// sort descending, most recent first

return another.date.compareTo(date);

}

}

Message, trong ví dụ 2, thường rất dễ làm. Nó ẩn đi một vài trạng thái bên trong của mình bằng cách cho phép truy cập ngày tháng và các liên kết như các chuỗi đơn giản, trong khi thể hiện chúng như các đối tượng được sắp xếp một cách rõ ràng (một java.util.Date và một java.net.URL). Nó là một Value Object (Đối tượng Giá trị) cổ điển, do vậy nó thực thi equals() và hashCode() dựa trên trạng thái bên trong của nó. Nó cũng thực hiện giao diện Comparable vì thế bạn có thể sử dụng nó để sắp xếp (theo ngày tháng). Thực tế, dữ liệu được phân loại từ điểm tin, do vậy mà điều này không cần thiết.

Mỗi thực thi trình phân tích sẽ cần đưa một URL đến điểm tin Androidster và sử dụng cái này để mở một kết nối HTTP đến trang Androidster. Hoạt động phổ biến này được mô hình hóa một cách tự nhiên trong mã trình Java sử dụng lớp cơ sở trừu tượng như trong Ví dụ 3.
Ví dụ 3. Lớp trình phân tích điểm tin cơ bản

public abstract class BaseFeedParser implements FeedParser {

// names of the XML tags

static final String PUB_DATE = “pubDate”;

static final  String DESCRIPTION = “description”;

static final  String LINK = “link”;

static final  String TITLE = “title”;

static final  String ITEM = “item”;

final URL feedUrl;

protected BaseFeedParser(String feedUrl){

try {

this.feedUrl = new URL(feedUrl);

} catch (MalformedURLException e) {

throw new RuntimeException(e);

}

}

protected InputStream getInputStream() {

try {

return feedUrl.openConnection().getInputStream();

} catch (IOException e) {

throw new RuntimeException(e);

}

}

}

Lớp cơ sở lưu trữ feedUrl và sử dụng nó để mở java.io.InputStream. Nếu có bất kỳ sai sót nào, đơn giản nó thả một RuntimeException, sao cho ứng dụng dừng hoạt động một cách nhanh chóng. Lớp cơ sở cũng xác định một vài hằng số đơn giản cho tên các thẻ. Ví dụ 4 trình bày một số nội dung mẫu từ điểm tin, qua đó bạn có thể thấy được ý nghĩa của các thẻ này.
Ví dụ 4. Điểm tin XML mẫu

<?xml version=”1.0″ encoding=”UTF-8″?>

<!– generator=”FeedCreator 1.7.2″ –>

<rss version=”2.0″>

<channel>

<title>android_news</title>

<description>android_news</description>

<link>http://www.androidster.com/android_news.php</link&gt;

<lastBuildDate>Sun, 19 Apr 2009 19:43:45 +0100</lastBuildDate>

<generator>FeedCreator 1.7.2</generator>

<item>

<title>Samsung S8000 to Run Android, Play DivX, Take Over the

World</title>

<link>http://www.androidster.com/android_news/samsung-s8000-to-run-android-

play-divx-take-over-the-world</link>

<description>More details have emerged on the first Samsung handset

to run Android. A yet-to-be announced phone called the S8000 is being

reported …</description>

<pubDate>Thu, 16 Apr 2009 07:18:51 +0100</pubDate>

</item>

<item>

<title>Android Cupcake Update on the Horizon</title>

<link>http://www.androidster.com/android_news/android-cupcake-update-

on-the-horizon</link>

<description>After months of discovery and hearsay, the Android

build that we have all been waiting for is about to finally make it

out …</description>

<pubDate>Tue, 14 Apr 2009 04:13:21 +0100</pubDate>

</item>

</channel>

</rss>

Như bạn có thể thấy từ mẫu trong ví dụ 4, một ITEM tương đương với một thể hiện Message. Các nút con của mục chọn (TITLE, LINK và v.v..) tương đương các đặc tính của thể hiện Message. Vì bạn biết điểm tin trông như thế nào rồi và có sẵn tất cả các phần phổ biến, hãy xem làm thế nào để phân tách điểm tin này sử dụng các công nghệ khác nhau có sẵn trên Android. Bạn sẽ bắt đầu với SAX.

Sử dụng SAX

Trong môi trường Java, bạn có thể thường xuyên sử dụng SAX API khi bạn muốn có một trình phân tích nhanh và muốn hạn chế tối đa việc sử dụng (footprint) bộ nhớ ứng dụng của bạn. Điều đó khiến cho cho nó rất phù hợp cho thiết bị di động chạy Android. Bạn có thể sử dụng SAX API như là từ môi trường Java, mà không cần đến những thay đổi đặc biệt cần thiết để chạy trên Android. Ví dụ 5 trình bày một thực thi SAX của giao diện FeedParser.

Ví dụ 5. Thực thi SAX

public class SaxFeedParser extends BaseFeedParser {

protected SaxFeedParser(String feedUrl){

super(feedUrl);

}

public List<Message> parse() {

SAXParserFactory factory = SAXParserFactory.newInstance();

try {

SAXParser parser = factory.newSAXParser();

RssHandler handler = new RssHandler();

parser.parse(this.getInputStream(), handler);

return handler.getMessages();

} catch (Exception e) {

throw new RuntimeException(e);

}

}

}

Nếu trước đây bạn đã sử dụng SAX, thì cái này trông cũng khá quen thuộc. Như với bất kỳ thực thi SAX nào, phần lớn các chi tiết đều nằm trong trình xử lý SAX. Trình xử lý nhận các sự kiện từ trình phân tích SAX khi nó chạy nhanh qua tài liệu XML. Trong trường hợp này, bạn vừa tạo ra một lớp mới gọi là RssHandler và đăng ký nó như là một trình xử lý cho trình phân tích, như trong ví dụ 6.
Ví dụ 6. Trình xử lý SAX

import static org.developerworks.android.BaseFeedParser.*;

public class RssHandler extends DefaultHandler{

private List<Message> messages;

private Message currentMessage;

private StringBuilder builder;

public List<Message> getMessages(){

return this.messages;

}

@Override

public void characters(char[] ch, int start, int length)

throws SAXException {

super.characters(ch, start, length);

builder.append(ch, start, length);

}

@Override

public void endElement(String uri, String localName, String name)

throws SAXException {

super.endElement(uri, localName, name);

if (this.currentMessage != null){

if (localName.equalsIgnoreCase(TITLE)){

currentMessage.setTitle(builder.toString());

} else if (localName.equalsIgnoreCase(LINK)){

currentMessage.setLink(builder.toString());

} else if (localName.equalsIgnoreCase(DESCRIPTION)){

currentMessage.setDescription(builder.toString());

} else if (localName.equalsIgnoreCase(PUB_DATE)){

currentMessage.setDate(builder.toString());

} else if (localName.equalsIgnoreCase(ITEM)){

messages.add(currentMessage);

}

builder.setLength(0);

}

}

@Override

public void startDocument() throws SAXException {

super.startDocument();

messages = new ArrayList<Message>();

builder = new StringBuilder();

}

@Override

public void startElement(String uri, String localName, String name,

Attributes attributes) throws SAXException {

super.startElement(uri, localName, name, attributes);

if (localName.equalsIgnoreCase(ITEM)){

this.currentMessage = new Message();

}

}

}

Lớp RssHandler mở rộng lớp org.xml.sax.helpers.DefaultHandler. Lớp này cung cấp các thực thi mặc định, không thao tác cho tất cả các phương thức tương tự các sự kiện được tạo ra bởi trình phân tích SAX. Điều này cho phép các lớp con chỉ ghi chèn lên các phương thức khi cần thiết. RssHandler có một API bổ sung, getMessages. Cái này trả về danh sách các đối tượng Message mà trình xử lý thu thập được khi nó nhận các sự kiện từ trình phân tích SAX. Nó có hai biến trong khác, một là currentMessage cho thể hiện Message đang được phân tích, và một là biến StringBuilder gọi là builder lưu trữ dữ liệu ký tự từ các nút văn bản. Các biến này đều được bắt đầu khi phương thức startDocument được dẫn ra khi trình phân tích gửi sự kiện tương ứng cho trình xử lý.

Hãy xem phương thức startElement trong ví dụ 6. Phương thức này được gọi mỗi khi bắt gặp thẻ mở trong tài liệu XML. Bạn chỉ cần quan tâm khi nào thẻ đó là thẻ ITEM. Trong trường hợp đó, bạn tạo ra một Message mới. Bây giờ hãy nhìn vào phương thức characters. Phương thức này được gọi ra khi bắt gặp dữ liệu ký tự từ các nút văn bản. Dữ liệu dễ dàng được thêm vào biến builder. Cuối cùng hãy xem phương thức endElement. Phương thức này được gọi ra khi bắt gặp thẻ kết thúc. Đối với các thẻ tương ứng với các đặc tính của một Message, giống như TITLELINK, đặc tính thích hợp được thiết đặt trên currentMessage sử dụng dữ liệu từ biến builder. Nếu thẻ kết thúc là một ITEM, thì currentMessage thêm vào danh sách Messages. Đây là sự phân tích SAX rất điển hình; ở đây không có gì là duy nhất đối với Android. Vì thế nếu bạn biết cách viết một trình phân tích SAX Java, thì bạn biết cách viết một trình phân tích SAX Android. Tuy nhiên, Android SDK có bổ sung thêm một số tính năng thuận tiện vào SAX.

Các bài liên quan:
Làm việc với XML trên Android – Phần 2