Mid February we organized a small Sprint at Nuxeo.
The point of "Nuxeo's Sprint" is basically to allocate two days to let the geeks (from Nuxeo's team or from a partner company) do some digging on technical developments of their choice.
One of the selected topics was "Build a Nuxeo DM client for Android".
5 of us worked on that topic:
- 3 guys from Nuxeo
- 2 guys from Smart&Soft
The main goal for us was to learn about Android development and understand the challenges and possibilities.
Since the Smart&Soft guys are specialized on Android development and had the good idea of publishing an OpenSource framework called Droid4Me, we used their experience to get us started quickly.
In fact we even had a quick training session to introduce Android development. :)
Integrating Content Automation in Android
Of course the point of our Android Application is to fetch data from the Nuxeo Server.
Logically we chose to use Content Automation REST API for that because:
- Automation API is powerful and easily extensible
- Marshaling based on JSON is easy to manipulate even from a small device with few hardware resources
(CMIS being based on SOAP and ATOMPub seemed too expensive for efficient Android integration)
- we already have a nuxeo-automation-client java client lib to encapsulate all API calls and provide high level business objects
The first step was to have a version of nuxeo-automation-client that runs on the Android platform.
For that we needed to introduce some changes because:
- Android SDK uses org.JSON by default whereas we were using net.sf.JSON
- we did not want the dependency on javax.mail for MultiPart encoding/decoding
(just googling javax.mail and Android convinced us that we wouldn't have time to handle that during the Sprint)
- we needed to add in the client lib a way to plug a cache system
(see later for more details)
This integration was very quick to setup and we had a first version of the Android App displaying documents fetched from Nuxeo only a few hours after we started coding.
To be honest we also faced several different build issues (more on this later), but at least startup was quick and efficient.
GUI and Asynchronous Webservices calls
Android development, at least from what we saw, is mainly GUI driven.
This implies that you have to be careful how you manage the webservices calls:
- you don't want the webservices calls to freeze the GUI
- you don"t want the network errors to have an impact on the GUI
This means that all remote services calls have to be made in a separated thread and the results, when available, have to be pushed to the UI thread.
For that matter, the Droid4Me framework did all the heavy lifting for us.
In fact, Droid4Me provides base classes that:
- extend the Activity life-cycle so that you can have some processing transparently managed asynchronously and pushed back to the UI thread when ready
- manage several thread pools for asynchronous processing (WebService calls, images downloading ...)
- manage the loading / wait icons
- manage error handling
This means we did not really have to handle this, just filling out the required abstract methods,
- one method for business objects retrieval
- one method for pushing the business objects to the UI
was enough to have a workable and responsive application.
Of course one of the points of the Android integration was to be able to also run in off-line mode.This is one of the key advantages of running a native application versus using a simple Web client.
In the standard Nuxeo approach we would probably have chosen to define a business objects level cache system. But Content Automation API marshaling being based on JSON, simply caching the HTTP response stream ended up being simpler and more efficient:
- Business object level caching would require adding another marshaling layer
(that would have been based on JSON too)
- caching the response stream is very efficient
- all the marshaling code is already in the nuxeo-automation-client lib
- it works the same for all kinds of return types (Blob, document, tasks, ...)
Since this caching logic (caching response streams) was part of the Droid4Me framework, we used directly what was provided by the framework to cache Automation calls in an SQLLite database.
In addition we added a disconnected mode to the automation client, so that we can transparently fetch cached object via Content Automation API.
Building Java code for Android platform
This was probably one of the most frustrating points on Android during the Sprint.
The initial startup was very fast, but we lost a lot of time figuring out how to solve the different build issues we encountered.
Here is a brief summary of the problems we faced:
Using standard Java dependencies
Our first build of nuxeo-automation-thin-client was a pure maven build, using standard Java dependencies on org.json, Apache common HTTP libs ...
The code compiled perfectly, but from time to time (not 100% deterministic) we had deployment issues during the byte-code translation to Dalvic (i.e. during the deployment on the Android Device).
The problems were mostly linked to some differences between the Apache classes found in the Android SDK and the original ones found is "official" maven artifacts.
Using android.jar maven artifact
Since we spotted some differences between the content of the Android SDK and some of the standard libs, we tried to build against the Android SDK jar that can be found on maven.
This improved the situation, but there was still some deployment issues.
The problems seems to tied to the fact that Android SDK jar are not redistributable, so the "mavenized" jar is rebuild from the source, it may explain the issues we encountered.
(See the comments in pom).
Using SDK provided android.jar
We ended up building against the android.jar provided by the Android SDK.
This solved all deployment issues and allowed us to spot some additional code differences between what was in the standard maven android jar and the one provided by the SDK.
The main drawback of this solution is that you can no longer run bare Java unit tests since the SDK android.jar contains a lot of stubs code (delegating calls to native android API). So you can do unit tests, but only in a real Android environment or you have to change the test classpath to override the classes provided by the SDK by real implementations.
Another issue is that for the maven build, we have to use the System scope and an environment variable for pointing to the Android Jar installed locally by the SDK.
General feedback on Android development
Android ADT plug-in for Eclipse provides a lot of tools:
- simple wizards
- integrated debugging
- reliable device emulation
Basically, once we had fixed the build issues and where able to build our artifacts using maven and integrate them into CI, we felt almost at home.
An important feature provided by Eclipse environment is the ability to do easy refactoring of the code. This is especially important when you start coding in a new environment where you probably don't get everything right from the beginning.
About the sample application we built
What it does
Given the amount of time we invested we have a very basic sample application.
Here is a brief feature list :
- fetch document lists from Nuxeo DM using content automation
(for now "my documents", "my working list", the access to the saved search is still in the todo list)
- browse the Document Repository
- fulltext search on Nuxeo
- provide several views on documents
- simple view on meta-data
- dedicated views for Pictures and Notes document types
- very basic view on audit logs of a Document
- download files
- download/view PDF conversion of the files
- list Tasks
- management of caching and off-line mode
- simple preference screen
Source code is available on Nuxeo's GitHub.
What it looks like
Here are some sample screenshots from the Android Application.
|Home Screen||Documents listing||Document View|
|Picture View||Note View||Tasks|
How to test it
The sample application is not published on Android Market, we may want to do that in the future, but for now the application is probably still too basic.
You can directly download the APK from our CI chain.
(you have to allow external APK installation on your device, see : Parameters / Application / Unknown sources => allow).
In order to test, you will need a recent Nuxeo DM instance (5.4.1-SNAPSHOT).
For that purpose we provide a demo server that is configured by default in the application.
server : http://android.demo.nuxeo.com/nuxeo/
login : droidUser
password : nuxeo4android
Remaining identified tasks
GUI is a very important aspect of Mobile application.
Unfortunately, we didn't spend much time on this and since we had no graphic designer available at the time of the Sprint, but most graphic resources can easily be improved.
Add support for saved search
This is mainly about adding the required high level Content Automation APIs in Nuxeo, then plugging into the Android client will be very simple.
Add support for Write operation
Ideally we want to be able to create and edit documents, even when offline.
Dig on Android Unit testing
Unit testing is one of the tools we lacked the most during the Android Sprint.
It looks like the right way of doing Unit tests on Android implies using the Android Framework and creating test Activities which allow us to test the Android code inside an emulation device.