Android threading and database locking

后端 未结 7 1655
夕颜
夕颜 2020-12-07 10:31

We are using AsyncTasks to access database tables and cursors.

Unfortunately we are seeing occasional exceptions regarding the database being locked.

相关标签:
7条回答
  • 2020-12-07 11:02

    Are you talking of a single user action that, inside your program, causes multiple threads to be run, more than one of which may be accessing the database in update mode ?

    That's bad design, period. There is no way for you to know in which order the threads will be scheduled by your OS (/VM), and therefore there is no way for you to know in which order the database accesses will happen, and that is very likely to imply that there is no way for you to know that database accesses will always happen in the order that you are expecting.

    All database accesses generated by/coming from some user action should all be done in one single thread.

    0 讨论(0)
  • 2020-12-07 11:06

    Before some code, let's resume some of the approachs:

    • Semaphores: by far the best solution presented. It goes in the heart of the problem: resource sharing! It will treat the locking of the database access, avoiding conflicts (database is locked).

    • Java synchronization: A kind of semaphore implementation, but less sofisticated. Using synchronized you will not easily solve some cases involving transactions.

    • ContentProvider: implement ContentProvider solve the problem only for some cases (or sweep the problem under the carpet). You'll yet face the same issues. The difference is that ContentProvider pattern will guide you to not make some commom mistakes when accessing Sqlite database. The ContentProvider docs says: "You don't need a provider to use an SQLite database if the use is entirely within your own application."

    • Almost mandatory: keep db instances local, call close() on the db in the same method in which it's opened using finally statements, close() on the cursors using finally statements, etc are almost mandatory to avoid problems using Sqlite.

    Let's show an example of the semaphore solution presented by Moss, which I took from CL. and improoved to cover transactions.

    class DataAccess {
        private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
        private final Lock r = rwl.readLock();
        private final Lock w = rwl.writeLock();
    
        public Data readSomething(int id) {
            Cursor c = null;
            r.lock();
            try {
                c = getReadableDatabase().query(...);
                return c.getString(0);
            } finally {
                if (c != null) c.close();
                r.unlock();
            }
        }
    
        public void changeSomething(int id, int value) {
            w.lock();
            try {
                getWritableDatabase().update(...);
            } finally {
                w.unlock();
            }
        }
    
        private void beginTransactionWithSemaphores() {
            getWritableDatabase().beginTransactionWithListener(new SQLiteTransactionListener() {
                @Override
                public void onBegin() {
                    w.lock();
                }
    
                @Override
                public void onRollback() {
                    w.unlock();
                }
    
                @Override
                public void onCommit() {
                    w.unlock();
                }
            });
        }
    }
    
    0 讨论(0)
  • 2020-12-07 11:06

    You must be calling getWritableDatabase() from a function rather then the constructor of the db helper class. If the db helper class object is created with SQLiteDatabase.openOrCreateDatabase(DB_PATH, null); or similar and then getWritableDatabase() is called from a function, it will try to make a synchronous call to DB causing a DB lock exception.

    0 讨论(0)
  • 2020-12-07 11:14

    I solved this same exception just by making sure all my database opens have closes, and (more importantly) to assure this, making the scope of each database instance local ONLY to the method that needs it. ContentProvider is a good, safe class to use when accessing a db from multiple threads, but also make sure you're using good db practices:

    • Keep db instances local (no SQLiteDatabase class members!)
    • call close() on the db in the same method in which it's opened
    • call close() on the cursors you get from the db
    • listen to LogCat for any complaints that SQLiteDatabse might have
    0 讨论(0)
  • 2020-12-07 11:17

    We used a ContentProvider in the end. This appeared to clear up the problems.

    0 讨论(0)
  • 2020-12-07 11:24

    Take into account that SQLite databases are file based and are not intended to be able to be accessed in a multi-process way. The best procedure on mixing SQLite with multi-processing is using semaphores (aquire(), release()) in each database related access.

    If you create a Db wrapper that aquires/releases a global semaphore your DB access will be thread safe. Indeed this means that you could get a bootleneck because you are queueing the access to the DB. So in addition you could only wrap the access using semaphores if it's an operation that alters the database, so while you are alterin the db no one will be able to access it and wait until the write process has been completed.

    0 讨论(0)
提交回复
热议问题