Android persistence accelerated - small inhouse ORM - codecentric AG Blog

:

A person easily gets used to comfort and luxury. In every segment of life. Bigger apartment, better car, new phone, bigger kitchen sink… Those are all good things. But, a person easily forgets how it was before the progress happened. Nervousness in the home, low fuel economy, small screen, lots of dirty dishes…

This reminded me on times before ORM tools. Remember when you had to write a tons of SQL statements, compile them, execute them, read column by column, etc…? Tedious and not very funny to work on. Few of our first Android applications were developed exactly in this manner. We wanted to get used to the platform, get to know it’s APIs and, generally, learn as much as we can about it. This is always a good way to go when you are working with new technologies. But very soon we got tired of going on foot and it was clear that we need some kind of transportation, some vehicle.

Sure, there are ORM tools that can work on Android platform, but, again, we didn’t want third party libraries. We wanted to develop ourselves something which will immediatelly suit our needs. So, we started working on small persistence library that will, hopefully, prove to be no less than excellent.

Activity class is the alpha and the omega of Android development. Almost everything that an Android application does, happens inside of some activity. Activity provides system resources, system services, starts threads, handles UI events,… Because of this, we wanted to make our database easily accessible from every activity that will do any database related operation. At the same time, we wanted to separate persistence and ORM work from any activity class. I believe that many of you involved in Android apps development already had similar situation. So, if you are interested how we tackled the issue, read on and see what we came up with.

As I said, we needed shared and uniform database access across whole application. When it comes to sharing, there are several options that Android provides us. Since we needed only logical sharing, so to say, that is important only while the application is up and running, we decided to go for custom application context. When defining your application in AndroidManifet.xml, you can define attribute “android:name” of your application’s “application” tag. Value of that attribute should be the name of the class which extends android.app.Application and will be instantiated when the app is run. By doing this, we ensured presence of our custom global singleton application context, let us assume that it’s name is “PersistenceApplicationContext”.

In addition to that, we created “database helper” class (extends android.database.sqlite.SQLiteOpenHelper) and wired it up inside of our app’s context. In this early moment, we already have a uniform way to obtain “database helper” class where needed:

dbHelper = ((PersistenceApplicationContext) getApplication())
                                               .getDatabaseHelper();

dbHelper = ((PersistenceApplicationContext) getApplication()) .getDatabaseHelper();

Just to make things more convenient, we created abstract PersistenceActivity class and overrode it’s onStart() method:

@Override
protected void onStart() {
super.onStart();
dbHelper = ((PersistenceApplicationContext) getApplication())
                                               .getDatabaseHelper();
}

@Override protected void onStart() { super.onStart(); dbHelper = ((PersistenceApplicationContext) getApplication()) .getDatabaseHelper(); }

From now on, we just had to extend this class and we would have dbHelper ready when we need it. Of course, there is no need to extend this class by every activity. Only those activities that realy work with database should use it.

This approach solves one small problem – shared database access – but how to make it uniform?

In order to make it uniform, we have to have same interface for all kinds of entities we will persist. For that purpose we need “EntityHelper” class:

public EntityHelper(PersistenceApplicationContext ctx, Class cls) {
    ...
}

public EntityHelper(PersistenceApplicationContext ctx, Class cls) { ... }

This class is intended to by persistence layer and to be able to work with every type of entity we want to persist. With smaller or bigger changes, it’s interface should look like this:

public T findById(long id);
public List listAll();
public void saveOrUpdate(final T entity);
public boolean delete(long id);
public void deleteAll();

public T findById(long id); public List listAll(); public void saveOrUpdate(final T entity); public boolean delete(long id); public void deleteAll();

Looking at signatures of it’s methods, it is obvious why constructor takes Class as one of parameters – by using it, we can determine what is the class of the entity we are reading/storing/deleting and do the ORM job properly. Example of using it (inside of some PersistenceActivity) would be something similar to next fragment of code:

PersistenceApplicationContext ctx = (PersistenceApplicationContext)
                                                  getApplication();
EntityHelper personHelper = new EntityHelper(ctx,
                                                     Person.class);
Person p = personHelper.findById(1L);

PersistenceApplicationContext ctx = (PersistenceApplicationContext) getApplication(); EntityHelper personHelper = new EntityHelper(ctx, Person.class); Person p = personHelper.findById(1L);

CRUD operations are not the only benefit of personHelper from the example. EntityHelper classes are able to do a bit of object-relational mapping and save us time there to. We decided not to reinvent the wheel nor warm water, so we just used good old JPA annotations: @Entity, @Id and @Transient so far. But, in order to make EntityHelper instances able to do their ORM work, needed entity descriptors have to be prepared.

That information is prepared during the startup of the application. Upon it’s creating, our custom application context triggers the scanning process: persistent classes are scanned, meta data and ORM data is formed and stored in context. At this moment, it gets clear why EntityHelper constructor takes PersistenceApplicationContext as first parameter – by passing it in, we make EntityHelper able to retrieve any data created during the scanning that it needs. Additional feature built on top of all this is SQL generation: based on results of scanning process, SQL statements are generated and can be used when needed since those are also stored in application context.

I admit this sounds very familiar and has resemblance with one famous ORM tool, but I assure you that our idea was not to copy it nor to support many different database systems. We just want and need something that will ease up our work with SQLite on Android devices, make us faster, more efficient and less error prone.

Using this kind of library for really small and simple applications does not make too much sense. But, mobile devices are quickly becoming more and more powerful which will definitely lead to demand of more complicated software. People already need mobile applications fully capable to colaborate with existing (higly complicated) business systems and that is what make developing this kind of library worthwile.

PersistenceApplicationContext
PersistenceActivity
ExampleApplicationContext
DatabaseHelper
EntityHelper
Entity