0

I am making some tests with my database connection factory on Android.

public class ConnectionFactory {

    private Context context;
    private SQLiteDatabase w;

    private static DatabaseManager manager;

    public ConnectionFactory(Context context) {
        this.context = context;
    }

    public synchronized SQLiteDatabase getDatabase(){
        if(manager == null){
            DatabaseManager.init(new DatabaseHelper(context));
            manager = DatabaseManager.getInstance();
        }

        return w == null || !w.isOpen() ? w = manager.getDatabase() : w;
    }

    public synchronized void close() {
        if(w != null && w.isOpen()){
            manager.close();
        }
    }

}

my DatabaseManager (I made this class for scenarios of concurrent access to database)

public class DatabaseManager {

    private static SQLiteOpenHelper helper;
    private static DatabaseManager instance;
    private static int COUNTER = 0;

    private SQLiteDatabase database;

    private DatabaseManager(){}

    public static synchronized void init(SQLiteOpenHelper first) {
        if (instance == null) {
            helper = first;

            instance = new DatabaseManager(){

                @Override
                protected void finalize() throws Throwable {
                    helper.close();
                    COUNTER = 0;

                    helper = null;
                }

            };

        }
    }

    public static synchronized DatabaseManager getInstance() {
        if (instance == null) {
            throw new IllegalStateException("O método init deve ser chamado antes deste método.");
        }

        return instance;
    }

    public synchronized SQLiteDatabase getDatabase() {
        COUNTER++;

        if(COUNTER == 1) {
            Log.i("SIAEP", "====================== ABRINDO UMA NOVA REFERENCIA DE SQLITEDATABASE ======================");
            database = helper.getWritableDatabase();
        }

        Log.i("SIAEP", "====================== DATABASE MANAGER COUNTER 'getDatabase': "+ COUNTER +" ======================");

        return database;
    }

    public synchronized void close() {
        if(COUNTER != 0)
            COUNTER--;

        if(COUNTER == 0) {

            if(database.inTransaction()){
                database.endTransaction();
            }

            Log.i("SIAEP", "====================== FECHANDO A REFERENCIA DE SQLITEDATABASE ======================");

            database.close();
        }

        Log.i("SIAEP", "====================== DATABASE MANAGER COUNTER 'close': "+ COUNTER +" ======================");
    }


}

my class of test:

public class TesteActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Worker t1 = new Worker(this);
        Worker t2 = new Worker(this);

        t1.start();
        t2.start();
    }

    public static class Worker extends Thread {

        private Activity instance;

        public Worker(Activity instance) {
            this.instance = instance;
        }

        @Override
        public void run() {

            ConnectionFactory factory = new ConnectionFactory(instance);

            for (int i = 0; i < 100; i++) {
                SQLiteDatabase database = factory.getDatabase();
                factory.close();
            }
        }
    }

}

this works perfectly, but when I use transactions, I receive some exceptions:

    @Override
    public void run() {

        ConnectionFactory factory = new ConnectionFactory(instance);

        for (int i = 0; i < 100; i++) {
            SQLiteDatabase database = factory.getDatabase();

            database.beginTransaction();
            database.setTransactionSuccessful();
            database.endTransaction();

            factory.close();
        }
    }

excetion:

E/ACRA ( 9850): java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/data/seduc.ma.com.br.siaepmovel/databases/database.sqlite E/ACRA ( 9850): at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55) E/ACRA ( 9850): at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:503) E/ACRA ( 9850): at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:416) E/ACRA ( 9850): at seduc.ma.com.br.siaepmovel.test.TesteActivity$Worker.run(TesteActivity.java:45)

any idea why?

====

my questions is different because the problem occurs only with transactions.

user155542
  • 471
  • 3
  • 5
  • 19
  • Forgive me but you seem to be going out of your way to make sure that there is a conflict. – e4c5 Oct 05 '15 at 14:57
  • Possible duplicate of [Concurrent access to a SQLite database in Android - db already closed](http://stackoverflow.com/questions/10801119/concurrent-access-to-a-sqlite-database-in-android-db-already-closed) – e4c5 Oct 05 '15 at 14:58
  • Why aren't you using the provided SQLiteOpenHelper? http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html – Philio Oct 05 '15 at 17:36
  • I use, look on `DatabaseManager` class, on `init` method. – user155542 Oct 05 '15 at 18:18

1 Answers1

0

In your code you have only one reference to database object. If one of workers call close method, other workers can not use closed connection. You should call workers one after another. You should clear context reference in your class ConnectionFactory

Look at enter link description here

Adam Miśtal
  • 715
  • 4
  • 15