Dev Database Design - frankandrobot/Reminderer GitHub Wiki

AlarmManager

AlarmManager is a built-in Android class that does the dirty work of handling alarms. You pass it a PendingIntent and a time for the PendingIntent to go off.

Originally, I wanted to add every task to the AlarmManager. Unfortunately, the AlarmManager probably uses too many resources to manage, say, 1000 alarms. So while this is the easiest solution, it's not the smartest.

Taking inspiration from Google's Alarm Clock App, we can give AlarmManager only one alarm at a time---the next alarm that's set to go off. When tasks do not repeat, this is easy:

  1. return all tasks with due dates >= current time (this is a database call)
  2. the first task(s) are the next alarm(s) that go off
  3. do steps 1 and 2 every time you add a task, delete a task, an alarm goes off, etc.

The problem is with repeating tasks. Since these can repeat forever, the simplest solution is now:

  1. for each repeating task, find the next time its due
  2. return all tasks with due dates >= current time
  3. the first task(s) are the next alarm(s) that go off
  4. do steps 1--3 every time you add a task, delete a task, an alarm goes off, etc.

Step 1 is non-trivial. So an alternative is to find all tasks that are due in the next 24 hours and then re-update this database table after the 24 hours are up. This reduces the problem to the first scenario. The performance hit happens only once a day. Well call this table the daily table. ##The Tables The main table will contain all of the fields of the Dev Task Class, or at least enough information to reconstruct the task.

The daily table will contain just the necessary info to get the task due. So it'll have these columns:

  • ID (ID for daily table)
  • _ID (ID for task in main table)
  • time
  • type (warning, main, expire)

The reason there's an ID and _ID columns is because repeating tasks may appear more than once. _ID allows you to find the task in the main table.

There could also be another table containing the next task(s) that are set to go off.

##ContentProvider The database should be encapsulated inside of a ContentProvider. This way if we decide to open up tasks to other apps in the future, we've already done all the work.

The ContentProvider is Android-specific. It's methods are in terms of URIs and column projections. In other words, you can't directly add/query/update tasks to the database using the ContentProvider interface.

So we'll also need another class that's an interface to the ContentProvider. This interface is app-specific---you'll be able to add tasks to the database, etc.

Multi-threading

Encapsulating the database in a ContentProvider ensures that no matter who calls the database, calls will always run in the same process. However, you still have to watch out for threading issues. A simple strategy is to make the database methods synchronized. This is better but you still need to watch out for the following:

if entry exists in db
  update entry 

In between checking if the entry exists and updating the entry, another thread can delete the entry, making update entry behave unexpectedly.