[align=center](In the interests of consistency and clarity I'm using plusminus' tutorial format.)[/align]
What you learn: You will learn how to create and parse custom compiled XML files.
Difficulty: 1 of 5
What it will look like:
[align=center]
[/align]
Tested on Android 1.1, 2.0, emulator only.
Description:
In this tutorial we are going to create and parse custom compiled XML files, including resource references. This will help you avoid hard coding data in your .java files, while still allowing fast access via Android's built in compiled XML parser, XmlResourceParser.
1.) We need some resources:
In your res/xml directory (create an xml folder in /res if it's not already there), create a file named test.xml, and add:
Using xml Syntax Highlighting
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <pictag id="@drawable/my_pic1" mystr="Picture 1" myint="33" />
- <pictag id="@drawable/my_pic2" mystr="Picture 2" myint="44" />
- <artag id="@array/my_array" mystr="My integer array" myfloat="1.234" />
- <plaintag>42</plaintag>
- </resources>
Parsed in 0.002 seconds, using GeSHi 1.0.8.4
In your res/drawable directory add two .png images (of your choice), named my_pic1.png and my_pic2.png.
In your res/values directory create a file named arrays.xml, and add:
Using xml Syntax Highlighting
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <integer-array name="my_array">
- <item>1</item>
- <item>2</item>
- </integer-array>
- </resources>
Parsed in 0.001 seconds, using GeSHi 1.0.8.4
We don't actually use the data in the array or the images for anything, we just need something we can reference from within test.xml.
These XML files will be automatically compiled for you by the Android resource compiler (aapt) when you build your project.
2.) I won't cover the basics of pull parsing as it is quite similar to SAX parsing (there is a nice tutorial here). The format of a pull parser is:
Using java Syntax Highlighting
- while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
- if (xpp.getEventType() == XmlPullParser.START_TAG) {
- ; //Do stuff with START_TAG
- } else if (xpp.getEventType() == XmlPullParser.END_TAG) {
- ; //Do stuff with END_TAG
- } else if (xpp.getEventType() == XmlPullParser.TEXT) {
- ; //Do stuff with TEXT
- }
- xpp.next();
- }
Parsed in 0.031 seconds, using GeSHi 1.0.8.4
3.) In order to display our XML data, we will make an instance of TextView, and accumulate data as it is parsed into one big string using TextView's .append() method. This isn't very OO but it's easy to understand.
Using java Syntax Highlighting
- // Create a new TextView that we can accumulate and display our parsed data to.
- TextView tv = new TextView(this);
- tv.setText("");
Parsed in 0.030 seconds, using GeSHi 1.0.8.4
Android has its own XML pull parser, XmlResourceParser, specifically tailored to efficiently parse its internal compiled XML format. To use this parser, we create an instance and pass it our data file resource, R.xml.test:
Using java Syntax Highlighting
- XmlResourceParser xrp = this.getResources().getXml(R.xml.test);
Parsed in 0.034 seconds, using GeSHi 1.0.8.4
As an aside, if you'd like to parse non-compiled resources, such as those from the net, you should probably use SAX (for speed) or DOM (for versatility) instead. The Android pull parser is specifically geared to working well on compiled XML.
Most of our work will be done in the START_TAG block of our parser. In this block we will check each tag, look at each attribute of that tag, and retrieve the attribute's value in a suitable format. For example, for our XML tag:
Using xml Syntax Highlighting
- <pictag id="@drawable/my_pic1" mystr="Picture 1" myint="33" />
Parsed in 0.001 seconds, using GeSHi 1.0.8.4
We will check to see if the tag name is pictag. If it is, we will look at the id tag and get its value as a resource id, look at the mystr tag and get its value as a string, and finally look at the myint tag and get its value as an integer. Values will be accumulated to our TextView as we go:
Using java Syntax Highlighting
- String s = xrp.getName();
- if (s.equals("pictag")) {
- // Get the resource id; this will be retrieved as a resolved hex value.
- int resid = xrp.getAttributeResourceValue(null, "id", 0);
- tv.append("Attribute id has value " + Integer.toHexString(resid)
- + " from tag " + s + "\n");
- // Get our custom string attribute.
- String sn = xrp.getAttributeValue(null, "mystr");
- tv.append("Attribute mystr has value " + sn + " from tag " + s + "\n");
- // Get our custom int attribute.
- int i = xrp.getAttributeIntValue(null, "myint", 0);
- tv.append("Attribute myint has value " + i + " from tag " + s + "\n\n");
- }
Parsed in 0.038 seconds, using GeSHi 1.0.8.4
A useful feature is that @drawable/my_pic1 is retrieved as a hex value (corresponding to the resource's R.java value). This gives you a handy way to mix and match Android-specific resources and resource references with your own.
The true strength of the above block is that calls to .getAttributeXXX() pull directly from the compiled XML resources (as created by aapt at build time). This means you get the versatility, maintainability and readability of XML combined with the speed of parsing compiled binaries.
4.) Incorporating the above code blocks, and adding parsing for the other tags gives us the core of our parser:
Using java Syntax Highlighting
- // Get the Android-specific compiled XML parser.
- XmlResourceParser xrp = this.getResources().getXml(R.xml.test);
- while (xrp.getEventType() != XmlResourceParser.END_DOCUMENT) {
- if (xrp.getEventType() == XmlResourceParser.START_TAG) {
- String s = xrp.getName();
- if (s.equals("pictag")) {
- // Get the resource id; this will be retrieved as a
- // resolved hex value.
- int resid = xrp.getAttributeResourceValue(null, "id", 0);
- tv.append("Attribute id has value " + Integer.toHexString(resid)
- + " from tag " + s + "\n");
- // Get our custom string attribute.
- String sn = xrp.getAttributeValue(null, "mystr");
- tv.append("Attribute mystr has value " + sn + " from tag "
- + s + "\n");
- // Get our custom int attribute.
- int i = xrp.getAttributeIntValue(null, "myint", 0);
- tv.append("Attribute myint has value " + i + " from tag "
- + s + "\n\n");
- } else if (s.equals("artag")) {
- // Get the resource id; this will be retrieved as a
- // resolved hex value.
- int resid = xrp.getAttributeResourceValue(null, "id", 0);
- tv.append("Attribute id has value " + Integer.toHexString(resid)
- + " from tag " + s + "\n");
- // Get our custom string attribute
- String sn = xrp.getAttributeValue(null, "mystr");
- tv.append("Attribute mystr has value " + sn + " from tag "
- + s + "\n");
- // Get our custom float attribute.
- float f = xrp.getAttributeFloatValue(null, "myfloat", 0);
- tv.append("Attribute myfloat has value " + f + " from tag "
- + s + "\n\n");
- } else if (s.equals("plaintag")) {
- // Get the element tag name here; the value will be
- // gotten on the next TEXT event.
- tv.append("Tag " + s + " has value ");
- }
- } else if (xrp.getEventType() == XmlResourceParser.END_TAG) {
- ;
- } else if (xrp.getEventType() == XmlResourceParser.TEXT) {
- // Get our value from the plaintag element.
- // Since this is a value and not an
- // attribute, we retrieve it with the
- // generic .getText().
- String s1 = xrp.getText();
- tv.append(s1 + "\n\n");
- }
- xrp.next();
- }
- xrp.close();
Parsed in 0.046 seconds, using GeSHi 1.0.8.4
Unlike a normal pull parser it is recommended that you call .close() on XmlResourceParser. The world won't end if you don't, but for resource constrained systems any hint to the system that you are done with a particular object or component is a good thing.
On a related note, for clarity I've used xrp.getEventType() when I need the parse event, but for performance this should really be cached locally (see Android Best Practices, here).
The id attribute is a special case for XmlResourceParser. Technically, instead of calling it id you can call it anything you like, but if you leave it as id, there are a couple of other .getXXX() convenience methods that you can use:
Using java Syntax Highlighting
- // Returns 0 unless attribute name is "id".
- resid = xrp.getIdAttributeResourceValue(0);
- Log.v(TAG, "resource id is: " + Integer.toHexString(resid));
- // Returns a null string unless attribute name is "id".
- String s1 = xrp.getIdAttribute();
- Log.v(TAG, "resource id string is: " + s1);
Parsed in 0.035 seconds, using GeSHi 1.0.8.4
The first one is the same as calling xrp.getAttributeResourceValue(null, "id", 0) as was done in the code earlier.
The second is useful if, for some reason, you want the unresolved string, in its @drawable/whatever format, say, for logging purposes.
The TEXT event is used to retrieve element values. I've included:
Using xml Syntax Highlighting
- <plaintag>42</plaintag>
Parsed in 0.000 seconds, using GeSHi 1.0.8.4
This is the last element in the XML file, and I've parsed it so you can see how it's done, but generally you will want to use attributes. While you can set up your XML files to use just element tags and values, since they are your own custom files, this isn't a good idea if you are planning on using compiled resources. Your XML will be much more verbose, but more importantly, you wont have access to the .getAttributeXXX() methods discussed above. In particular you will not be able to resolve resource references using .getAttributeResourceValue().
Here's an example:
(wrong way using values)
Using xml Syntax Highlighting
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <pictag>
- <id>@drawable/my_pic1</id> <-- will not resolve
- <mystr>Picture 1</mystr>
- <myint>33</myint>
- </pictag>
- <pictag>
- <id>@drawable/my_pic2</id> <-- will not resolve
- <mystr>Picture 2</mystr>
- <myint>44</myint>
- </pictag>
- </resources>
Parsed in 0.003 seconds, using GeSHi 1.0.8.4
vs:
(right way using attributes)
Using xml Syntax Highlighting
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <pictag id="@drawable/my_pic1" mystr="Picture 1" myint="33"/>
- <pictag id="@drawable/my_pic2" mystr="Picture 2" myint="44" />
- </resources>
Parsed in 0.002 seconds, using GeSHi 1.0.8.4
So unless you have some compelling reason to use values (e.g., legacy format, shared XML file) you should use attributes instead.
Takeaways:
- Use XmlResourceParser for compiled resources; these resources will almost always be local data files.
- Prefer SAX (for speed) or DOM (for versatility) for non-compiled or external data files.
- Prefer attributes over values; the real strength of XmlResourceParser is its compiled attribute handling.
- Feel free to mix resource references as appropriate, and use the "id" attribute tag to indicate this.
The Full Source:
"/src/your_package_structure/CompXML.java"
Using java Syntax Highlighting
- package com.example.compxml;
- import java.io.IOException;
- import org.xmlpull.v1.XmlPullParserException;
- import android.app.Activity;
- import android.content.res.XmlResourceParser;
- import android.os.Bundle;
- import android.util.Log;
- import android.widget.TextView;
- public class CompXML extends Activity {
- private static final String TAG = "CompXML";
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // Create a new TextView that we can accumulate and display our parsed data to.
- TextView tv = new TextView(this);
- tv.setText("");
- try
- {
- // Get the Android-specific compiled XML parser.
- XmlResourceParser xrp = this.getResources().getXml(R.xml.test);
- while (xrp.getEventType() != XmlResourceParser.END_DOCUMENT) {
- if (xrp.getEventType() == XmlResourceParser.START_TAG) {
- String s = xrp.getName();
- if (s.equals("pictag")) {
- // Get the resource id; this will be retrieved
- //as a resolved hex value.
- int resid = xrp.getAttributeResourceValue(null, "id", 0);
- tv.append("Attribute id has value "
- + Integer.toHexString(resid)
- + " from tag " + s + "\n");
- // Get our custom string attribute.
- String sn = xrp.getAttributeValue(null, "mystr");
- tv.append("Attribute mystr has value " + sn
- + " from tag " + s + "\n");
- // Get our custom int attribute.
- int i = xrp.getAttributeIntValue(null, "myint", 0);
- tv.append("Attribute myint has value " + i
- + " from tag " + s + "\n\n");
- } else if (s.equals("artag")) {
- // Get the resource id; this will be retrieved
- //as a resolved hex value.
- int resid = xrp.getAttributeResourceValue(null, "id", 0);
- tv.append("Attribute id has value "
- + Integer.toHexString(resid)
- + " from tag " + s + "\n");
- // Get our custom string attribute
- String sn = xrp.getAttributeValue(null, "mystr");
- tv.append("Attribute mystr has value " + sn
- + " from tag " + s + "\n");
- // Get our custom float attribute.
- float f = xrp.getAttributeFloatValue(null, "myfloat", 0);
- tv.append("Attribute myfloat has value " + f
- + " from tag " + s + "\n\n");
- } else if (s.equals("plaintag")) {
- // Get the element tag name here; the value is
- // gotten on the next TEXT event.
- tv.append("Tag " + s + " has value ");
- }
- } else if (xrp.getEventType() == XmlResourceParser.END_TAG) {
- ;
- } else if (xrp.getEventType() == XmlResourceParser.TEXT) {
- // Get our value from the plaintag element.
- // Since this is a value and not an
- // attribute, we retrieve it with the
- // generic .getText().
- String s1 = xrp.getText();
- tv.append(s1 + "\n\n");
- }
- xrp.next();
- }
- xrp.close();
- } catch (XmlPullParserException xppe) {
- Log.e(TAG, "Failure of .getEventType or .next, probably bad file format");
- xppe.toString();
- } catch (IOException ioe) {
- Log.e(TAG, "Unable to read resource file");
- ioe.printStackTrace();
- }
- // Display our accumulated string, containing the XML data we just parsed.
- setContentView(tv);
- }
- }
Parsed in 0.051 seconds, using GeSHi 1.0.8.4
Hope this helps!
XCaf



