May 17 2012
A customer asked me to prepare a sample Android project which converts docx to HTML.
The result is AndroidDocxToHtml
Since docx4j relies heavily on JAXB, the key to getting it working was getting JAXB – the reference implementation – to run on Android.
Android presents us with a number of challenges:
- it won’t let you add a jar which includes classes in the javax.xml namespace (which is where the JAXB API lives)
- JAXB uses JAXP 1.3 DatatypeFactory, but Android doesn’t provide it
- JAXB uses javax.activation.DataHandler
- Dalvik has a limit of 65536 method references per dex file
- it doesn’t support package level annotations (which JAXB uses, and which in docx4j supply namespaces)
Ill-advised or mistaken usage of a core class (java.* or javax.*)
You’ll get this message if you try to add a jar containing classes in java.* or the following javax packages:
Android doesn’t provide javax.xml.bind, and it won’t let you add it yourself. It forces you to re-package it. Just like on Google AppEngine, until Google eventually added it.
OK, done that; see https://github.com/plutext/jaxb-2_2_5_1/tree/android2 (the 2 in android2 is meaningless)
Repackaging is easy enough; the problem with it is that any library which uses the repackaged code, must also be changed. In the case of docx4j, this means a new branch, and ongoing maintenance.
JAXB uses JAXP 1.3 DatatypeFactory, but Android doesn’t provide it
com.sun.xml.bind invokes javax.xml.datatype.DatatypeFactory.newInstance, whereupon Android throws javax.xml.datatype.DatatypeConfigurationException: Provider org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl not found.
Easy solution: jar it up and provide it.
JAXB uses javax.activation.DataHandler
Easy solution: use the activation and additionnal jars from http://code.google.com/p/javamail-android/downloads/list
Dalvik limit of 65536 method references per dex file
This is more an issue running docx4j on Android than one related to JAXB, but it is worth noting. We’re running very close to this limit. Vote for the issue at http://code.google.com/p/android/issues/detail?id=7147
Also, you may need to give Eclipse more heap space (symptom is ‘you get Unable to execute dex: Java heap space’). In eclipse.ini, I used:
In Eclipse, Windows > Preferences > General > Show Heap Status gives you an entry on the bottom row which is useful.
Just when I thought it would all work…
I found that my XML was not unmarshalling, because it contains namespaces, and for some reason the objects in my JAXB were being read as not having any.
The problem is that Android doesn’t support package annotations: http://code.google.com/p/android/issues/detail?id=16149 (vote), but JAXB needs to read them. For example:
I ended up devising a simple minded way to tell JAXB about these programmatically. See Context.java. Hmmm, I probably should have created my own RuntimeInlineAnnotationReader implementation (Google ‘JAXBIntroductions’).
That done, it more or less works (if you need support for other package level annotations, you’ve got a bit more to do). The re-packaged JAXB is here. You can build it using ant -f build-repackaged.xml dist
It should work on Android 3 or 4.
To use it, where your code would otherwise import javax.xml.bind, use ae.java.xml.bind.