0

I've written a XML-Parser in Java using the SAXParserFactory (Code below). On my laptop it needs <1 second, but on my Android phone (S3 mini) it takes around 30seconds for the same file.

It parses a list of collectible cards. The xml-File is ~5MB and while parsing I create ~15000 Card Objects, which only contain Strings and are stored in a HashMap. When finished the App requires ~6MB RAM.

Using logcat I noticed that the GC is called quite often I think. I get the following output ~200 times:

02-05 16:03:51.460  21736-21738/magic.icebrothers.de.magicsimulatorandroid D/dalvikvm﹕ GC_CONCURRENT freed 704K, 15% free 11791K/13844K, paused 3ms+3ms, total 108ms
02-05 16:03:51.460  21736-21749/magic.icebrothers.de.magicsimulatorandroid D/dalvikvm﹕ WAIT_FOR_CONCURRENT_GC blocked 45ms

Is there a possibility to speed this up, by eg. using another XML-Parser, or using another data structure?

Or does it simply take so long to allocate the memory for this many objects?

I have tried to serialize the HashMap, but even loading this takes almost the same time (~25sec).

Thanks in advance!

private static class CardHandler extends DefaultHandler {

    private Card transform = null;
    private Card currentCard = null;
    private String currentElement = null;
    private HashMap<String, String> currentAttributes;

    public CardHandler() {
        currentAttributes = new HashMap<>();
    }

    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        super.characters(ch, start, length);
        String characters = new String(ch, start, length);
        if (!characters.trim().isEmpty() && currentElement != null && currentCard != null) {
            switch (currentElement) {
                case "name":
                    currentCard.setName(characters);
                    cards.put(characters, currentCard);
                    break;
                case "cost":
                    currentCard.setCost(characters);
                    break;
                case "type":
                    switch (currentAttributes.get("type")) {
                        case "card":
                            currentCard.addCardType(characters);
                            break;
                        case "sub":
                            currentCard.addSubType(characters);
                            break;
                        case "super":
                            currentCard.addSuperType(characters);
                            break;
                    }
                    break;
                case "rule":
                    int no = 1;
                    try {
                        no = Integer.parseInt(currentAttributes.get("no"));
                    } catch (NumberFormatException ex) {
                    }
                    String reminder = null;
                    if (currentAttributes.get("reminder") != null) {
                        reminder = currentAttributes.get("reminder");
                    }
                    currentCard.addRule(no, reminder, characters);
                    break;
            }
        }
    }

    @Override
    public void endElement(String uri, String localName, String name)
            throws SAXException {
        super.endElement(uri, localName, name);
        currentElement = null;
    }

    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        currentElement = qName;
        for (int i = 0; i < attributes.getLength(); i++) {
            currentAttributes.put(attributes.getQName(i), attributes.getValue(i));
        }
        switch (currentElement) {
            case "card":
                currentCard = new Card();
                break;
        }
    }

}
Lars
  • 1
  • Use Traceview to determine precisely where you are spending your time. – CommonsWare Feb 05 '15 at 17:08
  • @CommonsWare: Thanks for the hint, but I don't know how to evaluate this. Looking at exclusive time values, it is distributed over many methods, the heaviest is CardHandler.characters with 15% and ExpatParser.appendBytes with 13%, String and String.trim each have ~5%, the others <5%. In terms of Inclusive Times the CardHandler.characters takes 50% and the start/endElement Methods each about 25%. – Lars Feb 05 '15 at 17:49
  • OK. On the whole, disk I/O on Android is a lot slower than on a desktop, because on-phone flash storage is a lot slower than a hard drive (let alone an SSD). – CommonsWare Feb 05 '15 at 17:52
  • At least don't continuously allocate same String objects, that would lessen the amount of memory and garbage collection. Define your static strings as static final global vars. As for other parsers, you can't be faster as a Sax, but you could do use a library, so your parsing is less error prone. Try e.g. http://simple.sourceforge.net/ – einschnaehkeee Feb 05 '15 at 19:36

0 Answers0