Mackovich
Mackovich

Reputation: 3573

Android Cursor : database being modified as it is being read

My application is that of a SMS app to send and receive SMS but with the following difference: all messages are sent, via Bluetooth to your PC where my other App (in C# and WPF) will allow the user to write and receive SMS.

My application is not set as the default SMS app and is retro-compatible all the way back to API Lv 9.

When the user needs to load all his SMS to send them over to the PC, I am using Cursor to read from the internal SMS Database :

final Uri uriSMSInbox = Uri.parse("content://sms/inbox");
final Uri uriSMSSent = Uri.parse("content://sms/sent");
final Cursor cursorInbox = this.context.getContentResolver()
       .query(uriSMSInbox, null, null, null, null);
final Cursor cursorSent = this.context.getContentResolver()
       .query(uriSMSSent, null, null, null, null);

I have created a Message class to represent a SMS (with immutable final fields for ID, content, address, timestamp mainly). While using cursor.moveNext() I fill a list of Message which will represent all the stored SMS.

As of now, I can send and receive messages, everything works just fine.

But the thing is, when the PC app sends a message, it creates a Message object and send it via Bluetooth to the Android App. But how can I set its immutable ID ? It ought to match the one it will have in the internal SMS database !

So I try to query the database each time I send a message ... problem is the internal database is not always updated immediately. So "my" message does not exist ... yet ! The time it takes to be updated can vary a lot depending on the end-user device (hardware, Android version etc.).

So I can set the ID myself. I can ask the database what is the last ID, but what if the user exchanges a lot of SMS ? What if there are dozen of SMS being recieved or sent ... how can I be sure the ID I set is the one it will be in the database ?

I need to synchronize the SMS database between the phone and the PC so that the user does not have to send all the SMS each time he launches the Android app: he should be able to sync the missing messages (if he chatted outside my app).

What better and faster way than using the IDs to quickly compare and check ?

So here are my questions :

  1. Does Android (or the SMS default app in 4.4+) update the internal SMS database in an asynchronous way or does it lock it preventing multi-threading from accessing and (more importantly) modifying it in order to prevent race conditions ?

  2. Does using the Content Provider returns a Cursorrepresenting a sort of "copy" of the database at the given moment which would, therefore, ignore any modification if modifications occur ?

  3. Or does it lock the database, meaning Android (or the default SMS App) waits until I am done reading to update it ?

One solution would be to set a timer that will, from time to time, regularly check the SMS database and properly set the IDs. Is that a good way to go ?

Upvotes: 0

Views: 217

Answers (1)

Jim
Jim

Reputation: 10278

To answer your questions:

  1. in 4.4+, Android does not perform the update, it only creates permissions around which app is allowed to perform the update. The app creator could, conceivably, write unsafe, multithreaded code, just as they could before 4.4. This is/was not common, but is possible.

  2. The ContentProvider does return a Cursor and along with it, it can have observers that invalidate and re-query if the underlying data is updated. So if you want, you can register to receive notifications of those updates. Presuming you actually want the ID's to match the database, then you can't know the ID until the data is inserted - which means you need to wait until this happens. This may mean some messages are not synchronized as fast as you want, but they will be at some point.

  3. The DB is not locked while you have a Cursor open and are reading the DB. Updates can occur that can invalidate your Cursor. Generally, you should register to update the Cursor otherwise before reading a row, it could be deleted and then the Cursor will throw an exception.

Upvotes: 1

Related Questions