This the second delivery in a series of articles targeted at showing developers how db4o (an open source database that leverages today's object-oriented languages, systems, and mindset) is being used in several Android projects to avoid all the pitfalls and hassles of object-relational mapping while benefiting from an concise and straight forward way to evolve a domain model which, in the end, translates into faster, easier upgrades for users.

There are many benefits to using an object database like db4o, including easier code maintenance, and the ability to create applications based on more complex data models. Unlike in rigid, predefined SQL tables, you can store dynamic, free-form data, which can be changed or amended any time. In addition, db4o allows for data replication, another missing element in Android's software stack.

We'll take a look at the code in these projects to learn how developers leverage object database technology on their apps and also use the opportunity to introduce key concepts about db4o.

On the first article in the series we reviewed some aspects of project DyCaPo, a system that facilitates the ability of drivers and passengers to make one-time ride matches close to their departure time. This time we'll take a look at QuiteSleep, an Android project by César Valiente which is powered by db4o and is both open source and available in the Android market.

QuiteSleep's main task is to silence calls from contacts that have been previously blocked for a given time frame. Once the rejected call is finished QuiteSleep will restore the phone to it's normal operation state (restoring previous volume and vibration settings). Additionally, the app allows you to send a notification to blocked callers via e-mail or SMS.

According to César adding db4o to the project was straight forward: you just add the db4o jar file as any other external library in Eclipse (even for an Android project). Once this is done the full functionality of the db4o database is available to the Android application. During development the db4o version used in the project was db4o-7.12.132.14217. When you download db4o for Java you'll see that there are quite a bunch of jar files. This is because (starting with version 7.7) db4o was splitted so you can add exactly what you need in your project and decrease the library's footprint. If you choose the jar file where the name ends iwith "-all.jar" you'll be addding a version that has everything in it (no other db4o related jar files will be necessary).

In QuiteSleep db4o runs in embedded client/server mode which is a sort of an hybrid between a pure embedded option and networked C/S. When db4o acts as embedded server all communications are internal and fast (no networking takes place) but, as opposed to the simple embedded mode, many local clients can't connect to the database at the same time (ie. different clients can start different transactions on the db). In this applications the server is exposed as a singleton and one or more clients connect to it.

2. The sole instance of the database server is created here when receiving the first database client request. Once created the server instance is accessed as a singleton and will take care of new client requests according to the application's needs.

The database clients are responsible for doing queries, updates, deletions, etc. QuiteSleep provides a DB client class that rely on companion classes for performing these operations.In the code below you can see QuiteSleep's DB client class implementation:

/** * This function return the ObjectContainer used like as clientDDBB for * the application. * @return The ObjectContainer used like as clientDDBB * @see ObjectContainer */ public ObjectContainer getObjectContainer() { return clientDDBB; }

As you can see in the code, the DB client works with an ObjectContainer that is obtained by calling the DB server (in the static class ServerDDBB). Once we obtain the container we can use it to create the companion objects that perform operations such as Selects, Inserts, Updates and Deletes that allow us to work with the DB.

As an add-on, we can also instantiate the DB clients by passing an external ObjectContainer or even change the default DB server. These alternatives are not used in the application but are included as goodies for developers.

Once we obtain the DB client it's their responsibility to perform all the common operations of a database (selects, inserts, updates and deletes). Let's take a look at how the object that handles "selects" is created and how to submit a query to obtain all contacts in the database. The procedure for creating objects that handle inserts, updates and deletes is identical.

As you can see we pass the ObjectContainer in the DB client to the class constructor so we can build the queries and obtain the necessary data. the queries used in the code are called SODA queries, one of the three query types that db4o currently supports.

Application Lifecycle

During the application life-cycle there are several points where data is accessed (queries, inserts, updates, etc). Let's see how db4o is used by QuiteSleep during normal operation.

Contact Management

When QuiteSleep boots for the first time it performs what is probably the most important data operation in the app: it synchronizes all the contacts with db4o.

The app retrieves, via a content provider, the user's contact list and filters out all the contacts that have no associated phone number (they are not useful for QuiteSleep). The contacts with phone numbers are then stored in db4o. Once this is done db4o becomes the only data access point for the application.

This sync operation is performed in the background with the help of Java threads. Android dialogs are used to notify the user that the process is taking place while preventing user interaction (the process is critical to allow the app to work properly).

The sync process can later be performed by the user on-demand via the app's settings. During the sync process a series of objects are created that will be made persistent in db4o and participate in data access operations:

Contact
This class represents a typical contact (with a full name and a unique contact id). It also has a boolean attribute that shows whether this caller is in a blocked state in the application.

Phone
This class represents a phone number associated with a contact. It has a direct reference to a contact object described above. It also provides a boolean attribute to indicate whether this phone number is enabled to receive SMS notifications by the application.

Mail
This class represents e-mail addresses belonging to contacts. It also references a contact object directly (the owner of the e-mail data). It also provides a boolean attribute to indicate whether this e-mail is enabled to receive e-mail notifications by the application.

These objects are manipulated during operations such as adding blocked contacts, editing contact information and unblocking contacts.

During the contact blocking operation a new kind of object is created in the database:

Banned
This object is created when we mark a contact as blocked. Internally, this operation relates a Contact object with a Banned object and a Schedule object (the later specifies exactly when a contact is supposed to be blocked as explained below).

Every time we remove the blocking state for a contact the Banned object is deleted from the database while the Contact and Schedule objects remain untouched (no
cascade deletion is used).

Once the app creates all the objects associated with contacts (objects of types Contact, Phone, Mail, etc) and establishes the blocked state for them, the next step is to determine the active time frame for blocked contacts.

When any of these parameters is defined by the user, a Schedule object is created based on this information and saved in the database. The Schedule object is created only once and is never deleted (only updated when the schedule details are changed by the user).

As we mentioned before Scheduled objects are referenced by Banned objects.

Service Management

Once all the contact related data together with blocking information has been processed the app configures the notifications service (both SMS and e-mail) and other minor services. when this is the dome the main service loop is started and the app is ready to be used.

At this point the Settings object is created and stored in the database with parameters such as:

quiteSleepServiceState: true when QuiteSleep is up and running and has been activated

E-mail and SMS configuration attributes: such as recipient, body, subject, etc together with a boolean value that determines whether each type of notification is active

Event History

In this phase the user can access information about the recorded activities while the application was running. The available information consists of the contact name, phone number of the originating call, call date and time, number of messages (SMS, e-mail) that were sent.

In order to support this kind of logging a CallLog object is made persistent in db4o for every blocked call. A CallLog object references a specific contact and phone number combined with the specific date and time of the blocked call.

public void silentIncomingCall() {

try {

/* * Put the mobile phone in silent mode (sound+vibration) Here i put * this for silent all incoming call for salomonic decision that * silent all calls for later if the contact is banned let this * silent mode and if the contact isn't banned put in normal mode. I * use this because sometimes a ring second sound when an incoming * call. */ // putRingerModeSilent();

/* * Check if the mail service is running, if it is true * create a SendMail object for try to send one or more * email to the contact with the incoming number */ sendMail(incomingCallNumber, callLog, clientDDBB);

/* * Check if the sms service is running, if it is true create * a SendSMS object for try to send a SMS to the contact * with the incoming number */ sendSMS(incomingCallNumber, callLog, clientDDBB);

// get the nomOrder for the new CallLog int numOrder = clientDDBB.getSelects().countCallLog(); if (QSLog.DEBUG_D) QSLog.d(CLASS_NAME, "CallLog numOrder: " + numOrder);

// Set the parameters and save it callLog.setPhoneNumber(phoneNumberWhithoutDashes); callLog.setContact(contactBanned); callLog.setNumOrder(numOrder + 1);

Now let's take a look at how the application behaves and how the described objects work together and are made persistent when there's an incoming call.

Incoming Call

When there's an incoming call Android throws a "broadcast intent", a signal that alerts apps the call should be handled. QuiteSleep implements a "broadcast receiver" that listens to this broadcast message. Once the message is intercepted QuiteSleep performs a series of checks such as whether the phone is already muted and if the app itself is in active state.

If these checks are passed a background process is launched via an Android service that checks whether the incoming call number belongs to a blocked contact. This is when the CallLog object is created and stored in the database to later have a record for the call.

If the call was effectively silenced according to the specified day and time frame the independent threads are created to handle notifications (SMS or e-mail). This is performed before making the CallLog object persistent on db4o to keep a record of sent notifications.

Call Termination

Once a call has been silienced a hang-up event (another broadcast intent) will follow which is intercepted by the app via a different broadcast receiver. The wrap-up actions are then the following:

Check whether the phone is in mute mode via the app (if it's not this is an unrelated hang up event that shouldn't be handled)

Check whether the QuiteSleep service is active

Check whether a hang-up is already being processed by the app

If all the checks above are positive a final background thread handles restoring the phone to it's original state to allow for the normal handling of incoming calls.

Conclusion

In QuiteSleep db4o is up to the task of dealing with all persistence operations in the app and makes persistence related code simpler.

In César Valiente's own words:

db4o is an excellent option for Android apps that don't need to use the complexity of relational databases, with a few lines of code you can create a complete data model for your app, using queries, inserts, deletes, updates and index configurations... and more importantly, the class model in your app is the data model in your object database!!