SQLiteOpenHelper synchronization
Asked Answered
I

5

9

So I've come up with some idea and I'm wondering if it is realizable.

Let's say I've multiple tables(database models) each of them is represented by some class.I don't wont to use singleton pattern with the open helper so I've created some simple class to provide single instance of the database.My idea is that as long as all tables hold reference to SQLiteDatabase(returned by the open helper) they will all work with the same DB instance and probably won't be needed to synchronized the work with the database since the open helper do this.When the last table finish it's work the GC will collect the open helper (since the last reference will be weak reference) -> finalize() is called and I close the db during this method to prevent any warning from OS. My question is: Is this could work?Will it close automatically the DB and will it leak or throw some exception?

Here is my class:

public class DatabaseHelper {

private static WeakReference<SomeCustomOpenHelper> sDBOpenHelper;

private void notifyDBCreate(SQLiteDatabase db) {
    for (DBTable table : mTables) {
        table.onDBCreate(db);
    }
}

private void notifyDBUpgrade(SQLiteDatabase db) {
    for (DBTable table : mTables) {
        table.onDBUpgrade(db);
    }
}

public SQLiteDatabase getDatabase(boolean readOnly) {
    SomeCustomOpenHelper dbHelper = sDBOpenHelper.get();
    if (dbHelper == null) {
        dbHelper = new SomeCustomOpenHelper(context, name, factory, version, new DatabaseEventsCallback());
        sDBOpenHelper = new WeakReference<SomeCustomOpenHelper>(dbHelper);
    }
    if (readOnly) {
        return dbHelper.getReadableDatabase();
    } else {
        return dbHelper.getWritableDatabase();
    }
}

private class DatabaseEventsCallback implements IDatabaseEventsCallback {

    @Override
    public void onCreate(SQLiteDatabase db) {
        notifyDBCreate(db);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db) {
         notifyDBUpgrade(db);
    }

}

interface IDatabaseEventsCallback {
    void onCreate(SQLiteDatabase db);

    void onUpgrade(SQLiteDatabase db);
}

private static class SomeCustomOpenHelper extends SQLiteOpenHelper {

    private IDatabaseEventsCallback mCB;

    public SomeCustomOpenHelper(Context context, String name, CursorFactory factory, int version, IDatabaseEventsCallback cb) {
        super(context, name, factory, version);

        mCB = cb;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        mCB.onCreate(db);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        mCB.onUpgrade(db);
    }

    @Override
    protected void finalize() throws Throwable {
        this.close();
        super.finalize();
    }
}
}
Isaisaac answered 27/3, 2013 at 8:45 Comment(5)
Why exactly do you not want to use the singleton pattern for your OpenHelperSuzysuzzy
There are some topics which describe the singleton as antipattern.My 2 main reasons are it must stay in the memory during the application lifecycle and it can not be tested/extended.Isaisaac
there is nothing against using a weak reference in a singleton pattern, which seems to be basically what you are doing.Sweetener
It is not singleton pattern since I can have multiple database helper objects in all table classes which all will share one sqliteopenhelper.The idea is that they will all share one instance as long as anyone use that instance.When last sqliteopenhelper holder release that instance it will be gc-ed and probably properly db closed.My question is will it work like this or something will go wrong :)Isaisaac
yes, therefore your singleton is sqliteopenhelper, encapsulated in a weakref. it's a different way of doing it, but it still looks like a singleton to me in the sense it restricts the instantiation of a class to one object.Sweetener
S
2

Did not really know the answer neither, but got interested and looked it up.

The answer is written out properly here; http://blog.foxxtrot.net/2009/01/a-sqliteopenhelper-is-not-a-sqlitetablehelper.html

But basically the core of the info is;

I created three SQLiteOpenHelper classes, one for each table, even though they all referenced only a single database file.

Here is where everything fell apart. Android maintains Versions for databases based on the package it’s associated with, the name of the database, and the version number you provide. The package and name go into decided what the path on the device will be, while the version is stored (somewhere) on the device so that it knows when it needs to call an OpenHelper’s onUpgrade event handler. It turns out that if, in the SQLiteOpenHelper Constructor, it determines that the database already exists, it won’t call your onCreate or onUpgrade methods at all, even if the particular class which is making the call has never been called before.

Suzysuzzy answered 27/3, 2013 at 9:1 Comment(1)
My idea is that all use one open helper not one open helper per table.Isaisaac
W
2

I've been through the same issue when I was working on a project. I also went crazy on the doubt if the static instance was using enough memory and causing a considerable memory leak.

I'm not sure if creating a weak reference would guarantee that database instance would be collected. However a possible workaround could be : Assigning a null value to static database instance once your all database transaction is done and you've close the database. This might ensure that the database instance no more allocates any memory.

Let me know if this works or if there is a better work-around.

Witenagemot answered 4/4, 2013 at 9:56 Comment(0)
I
1

You can do so. As you say the locking should be happening on the SQLite and I've never heard issues around that so you should be fine with this. The only restriction you have is that all the tables will have to go into the same database since Android for now just allows you to have one file.

Closing the database is a different thing, that's why it is actually interesting to use the singleton pattern (you avoid closing + opening all the time). Nonetheless with your approach you just need to make sure to close the db whenever you are done with it. As far as I'm concerned this is not automatically done.

Additionally Lars Vogel has written extremely useful and detailed articles around DB access in Android. You might want to have a look there. http://www.vogella.com/articles/AndroidSQLite/article.html

Indefectible answered 1/4, 2013 at 21:35 Comment(6)
Yes but here closing the db is happening when GC collects the custom helper class.It will be collected when the last db object is freed since the only reference left will be Weak (of the outer class)Isaisaac
Note that getting rid of any object in memory that extends from any database open helper doesn't close the db for you. Actually the db will remain open if you don't do it. From the documentation in SQLiteOpenHelper(developer.android.com/reference/android/database/sqlite/…): "(Make sure to call {@link #close} when you no longer need the database.)"Indefectible
"Android for now just allows you to have one file." what do you mean ?Sweetener
As you can see in SomeCustomHelper class in finalize() method db is closed.Isaisaac
I wouldn't use finalize for that purpose. Note that you are not assured when and how the GC would take care of your object. For instance it can be that it takes much more time than expected. Additionally closeables are recommended to be taken care of as soon as the resource is not needed anymore. Check that for more info. developer.android.com/reference/java/lang/…Indefectible
You can also check out at android's source SQLitedatabase implementation ( android.googlesource.com/platform/frameworks/base/+/refs/heads/… ) search for the finalize() method.Isaisaac
I
1

you can use one open helper for all the table .i am using the single instance in my app also like this .

public static synchronized DatabaseHelper getInstance(Context ctx)
    {
        if (dbhelper == null) {

            dbhelper = new DatabaseHelper(ctx);
        }

        return dbhelper ;
    }
Icelandic answered 2/4, 2013 at 11:58 Comment(4)
so you are basically using a singleton. second sentence in the question reads I don't wont to use singleton pattern. Just sayinSweetener
then he can create every time new instance ..whats the problem.or may be i am not getting the question..thanksIcelandic
Creating multiple instances of sqliteopenhelper may throw exception when one instance is writing to db while other one is trying to read/write.Having only one instance at a time provides you some kind of synchronization. Let's first make the difference from singleton and single instance.And there is so many articles why singleton pattern should be considered as anti pattern.I'm not saying that it is useless since there is some cases which singleton is the best solution.But Im not sure if this case is necessary so Im asking could this work or it might broke somewhere.Isaisaac
I'm interested if you have a precise comparison between single instance and singletonSweetener
C
0

My question is: Is this could work?Will it close automatically the DB and will it leak or throw some exception?

NO it will not close automatically database , when your application will demand for DATABASE object and OS found some of your database instant are alive then Android framework try to connect that object reference (which is probably weak reference )

and i have to say , I don't recommend opening and closing a DATABASE on-demand or temporarily . It is always nice to open the DB up early and keep it open for the duration of your whole activity and close it when the activity gets finished or suspended .

Clear answered 5/4, 2013 at 6:25 Comment(2)
Probably you don't see the whole idea.OpenHelper holds reference to sqltiedatabase and provides synchronization.I will use only this reference as long as some1 is using it.If during the application lifecycle some1 is holding strong reference to the open helper it won't be closed.When no strong references left the last one will be in the wrapping class (and it's weak ref) so the gc will collect this db -> finalize -> close. Next time when some one invokes getDB() it will create new instance for open helper and it will provide the same instance as long as some1 is using it.Isaisaac
please refer that SO question #6906024Clear

© 2022 - 2024 — McMap. All rights reserved.