1

I have two parts of a program (the actual app and a BroadcastReceiver) that could possibly try to connect to and modify a SQLite database and a SharedPreferences file.

1) I know you can make multiple connections to a single SQLite database. I also read that SQLite can "lock" the database when it attempts to modify or read from a database (UPDATE, INSERT, etc), so how do I properly handle the case where 2 threads try to modify/read a single SQLite database at the same time?

Right now, I have have this code snippet in my database connection code/class:

private SQLiteDatabase myDatabase;
public boolean insert(ContentValues columnValueMap) throws Exception
    {
        long result = 0;
        int attempt =0;
        do
        {
            if(!myDatabase.isDbLockedByOtherThreads() && !myDatabase.isDbLockedByCurrentThread())
            {
                synchronized (myDatabase)
                {
                    result=myDatabase.insertOrThrow(TABLE_NAME, TEXT, columnValueMap);
                    if(result ==0 || result == -1)
                    {
                        return false;
                    }
                    return true;
                }
            }
            else
            {
                Thread.sleep(50);
            }
            attempt++;
        }while(attempt<=5);

        throw new Exception("Database Locked!");
    }

2) Do I also have to worry about simultaneous access for a SharedPreferences file (It doesn't matter which thread gets access first, I'm just worried about errors being thrown)?

Thanks.

[EDIT] Proposed change

public boolean insert(ContentValues columnValueMap) throws Exception
    {
        long result = 0;
        synchronized (myDatabase)
        {
            result=myDatabase.insertOrThrow(TABLE_NAME, SMS_TEXT, columnValueMap);

            if(result ==0 || result == -1)
            {
                return false;
            }
            return true;
        }
    }
RyanM
  • 5,680
  • 9
  • 45
  • 55

1 Answers1

4

First of all, databases have built in locking support, and it's turned on by default. If you want to be safe, call SQLiteOpenDatabase.setLockingEnabled(true).

As for access to the shared preferences, I would encapsulate all access to SharedPreferences (read and write) in small functions that all use synchronized. (Be sure that they are atomic).

EboMike
  • 76,846
  • 14
  • 164
  • 167
  • Thanks for responding. So, I shouldn't worry about multiple threads with SQLite? For instance, thread 1 will just wait for thread 2 to finish if both are editing the database because file locking is built-in? If so, why do methods "isDbLockedByOtherThreads()" and "isDbLockedByCurrentThread()" exist? As for SharedPreferences, I'll add synchronized to that code segment per your recommendation.Thanks. – RyanM Jul 13 '10 at 06:14
  • 1
    From the docs, setLockingEnabled() is used to "Control whether or not the SQLiteDatabase is made thread-safe by using locks around critical sections.". That implies that the OS does everything for you. (Btw, in general, I would never poll a lock like you did above - if you really have to synchronize something yourself, just create an Object and use notify/wait on it. Polls are typically a guarantee for sadness. Spinlocks should only ever be used in few cases where it makes sense. – EboMike Jul 13 '10 at 06:48
  • Ok, that makes sense. I've seen examples where dummy objects were used as the monitor in the synchronized block, but do I need to do that here? Is my proposed change in the edit above the better way of doing this? Thanks. – RyanM Jul 13 '10 at 07:24
  • synchronized(myDatabase) already locks the database object, so you don't need to do any further synchronization. This assumes that you only have one database object. If not, create a dummy lock object and use that instead. synchronized(lockObject) { everythingInHereIsAtomic; }. But agin, if setLockingEnabled() is set, you don't need to do anything at all in the specific case of SQLiteDatabase (I suppose your example now refers to the SharedPreferences object). – EboMike Jul 13 '10 at 07:33
  • Ok, I changed the edit section above to just show the synchronized block, and I set the setLockingEnabled(true). I should be set? – RyanM Jul 13 '10 at 07:43
  • 1
    This is now deprecated as of API 16. http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html – Jared Burrows Jun 15 '13 at 21:42