- Database schema creation.
- SQL queries.
- Data access (objects mapped to and from database).
- Business logic.
There is only a small amount (less than 10%) of Android-specific GUI and notification code.
Other than the code that creates the database schema (which is database-specific), all of the non-Android code is device- and OS-agnostic. It just needs a Java runtime.
I wanted to be able to easily reuse the portable code to run the application on a website, and on other Java-based handhelds.
The first step involved abstracting the database so that any database can be used, instead of just Android's binding to SQLite:
and also abstracting the logger:
Once that was done, I could test the database and logger independently from Android:
The portable business logic is in another set of projects:
Finally, the Android-specific code is in two more projects:
The inter-project dependencies look like this:
The CommonDatabaseTestUtils are broken out from CommonPortableTestUtils because they depend on JARs that aren't loaded on the device, so they can't be included in the dependencies for MyAppAndroidTests.
MyAppAndroidTests depends on target/classes and bin for reasons explained in http://jimshowalter.blogspot.com/2009/10/developing-android-with-multiple.html.
So was it worth it?
Well, yes, and no.
There are definitely advantages:
- I can write any other database-intensive application by cloning this setup and reusing the code in com.intuit.jimandlisa.common.*.
- The database-abstraction layer doesn't have the problems reported in Google Android issues #3302, 3296, and 3304.
- Having much of the code be regular, portable Java makes it easier to write coverage tests because EasyMock, JMockit, and Cobertura are available.
- This approach creates a lot of separate projects, and that's kind of a pain to set up.
- I have to maintain the database-abstraction layer.
- More work is required to mature the database-abstraction layer. (It currently doesn't support binding args or compound keys, all keys must be longs, etc.)
- Because the database-abstraction layer isn't a native binding, it's slower. For a cellphone app with a few database operations per hour, this doesn't matter, but it will matter on a website.
It might be better to model the application, and generate the code. With so many devices, OSs, and vendors, code generation might be the only pragmatic way to develop a cross-platform application.
Update: The sample code has been updated to add createSchema and updateSchema to AbstractDatabase in order to make execSql private.