I’ve been working on a few projects for the Android platform, which I intend to turn into applications. While I do plan to commercialize at least a few of these apps, I’m also looking for ways to improve the platform itself, and the body of knowledge surrounding it. One of those Body of Knowledge issues might seem fairly trivial, but it did catch me up for a few days.
As an aside, a large source of this error was due to the fact that I’ve been doing a lot of work with the LINQ-to-SQL ORM, which led me to make some assumptions about ORM design that in retrospect don’t make a lot of sense, and certainly didn’t work. But the reason they didn’t work was interesting, and worth sharing.
One of my applications is a Microblogging application with clean support for multiple services. The first version supports Twitter and and Laconica-based services, such as the TWiT Army. Currently, no such apps exist, and I’ve seen very few apps handle the multiple accounts situtation in a manner I consider to be reasonable. Because of this need to support multiple accounts, and a desire to normalize the database somewhat, I ended up with the following (basic) table structure.
+----------+ 1 -> n +---------+ n <- 1 +---------+
| accounts |------->| notices |------->| senders |
+----------+ +---------+ +---------+
| ^
| 1 -> n |
+--------------------------------------+
If SQLite supported Foreign Keys, I’d have set those up as well, but it doesn’t, and generally doesn’t need to. But these three tables should be able to handle any microblogging situation I can think of off the top of my head. However, every example of writing a SQLiteOpenHelper I was able to find only discussed creating a single table. Between this and my thought that each table should have it’s own class, I created three SQLiteOpenHelper classes, one for each table, even though they all referenced only a single database file.
Here is where everything fell apart. Android maintains Versions for databases based on the package it’s associated with, the name of the database, and the version number you provide. The package and name go into decided what the path on the device will be, while the version is stored (somewhere) on the device so that it knows when it needs to call an OpenHelper’s onUpgrade event handler. It turns out that if, in the SQLiteOpenHelper Constructor, it determines that the database already exists, it won’t call your onCreate or onUpgrade methods at all, even if the particular class which is making the call has never been called before.
This makes perfect sense, in retrospect. And, in fact, there are benefits to keeping all those methods a single class. For instance, it now makes sense to use slightly more complex SQL queries utilizing joins to get the data about a Sender from the notices table, where before I would likely have loaded the notices, and then for each record in the notices table, made a call into the senders table to get the data about the person who sent that message. This would have been far more inefficient, particularly if notices was allowed to reach a non-trivial size.
Basically, I needed to think about the SQLiteOpenHelper more like the LINQ-to-SQL Database Context than as a LINQ-to-SQL table object. One interesting idea from a software engineering standpoint (which may not be worthwhile in Android) would be to define the functions on each table in their own Interfaces, which would then be used to identify in which scope the OpenHelper is being used. I’m not intimately familiar with the sort of output Davlik would generate in this instance, so it’s possible this redirection may not be worthwhile, and most Android apps should have a good enough split between their Activities and their data layer, that it probably would save almost nothing, but it’s an interesting idea.
Hope this helps someone, I know I spent more time on this problem than it deserved.
Categories:
No TrackBacks
TrackBack URL: http://blog.foxxtrot.net/cgi-bin/mt-tb.cgi/298

4 Comments
Very interesting….
I never considered creating an independant class for each table but then honestly I always thought of the SQLiteOpenHelper as a “database level” class but now you have made me wonder if it would be better if these objects were somehow split up along the lines you describe.
Hi there,
i still don’t quite understand what the solution to this be.
should our onCreate and onUpgrade create all the table via sql althogether or should we still use a seperate implementation
Best REgards.
Kyith,
You have to have one SQLiteOpenHelper for the entire database, but what I’ll usually do is create separate objects that contain related methods to which I pass the handler to the SQLite database.
So, in the above example, I have a SQLiteOpenHelper which handles creating and upgrading the databse, and then a class for account management which handles all the account-related tasks, which takes a handler to the SQLite database opened by the OpenHelper, and defines the methods I need in that case, and a class for notice management that handles those functions, etc.
This gives me the single SQLiteOpenHelper I required, and allows me to not have one enormous monolithic class to provide methods to handle all database-related tasks. There is a bit of overhead in this approach, but in my opinion, it’s more maintainable.
Hi Jeff,
One year later, I’m facing the same issue as you did. I thought the OpenHelper as “one per table”, and of course, my 2nd table would never create. I was starting to wonder about the limits imposed by versioning when I found you blog article. Saved me a lot of time. This means I have to declare all my tables at the same time and if I want to add a table, increase the version… Bit more complicated than I though. thx a lot anyway!