Would it be OK to have a single instance of SQLiteOpenHelper as a member of a subclassed Application, and have all Activities that need an instance of SQLiteDatabase get it from the one helper?
Having a single SQLiteOpenHelper
instance can help in threading cases. Since all threads would share the common SQLiteDatabase
, synchronization of operations is provided.
However, I wouldn't make a subclass of Application
. Just have a static data member that is your SQLiteOpenHelper
. Both approaches give you something accessible from anywhere. However, there is only one subclass of Application
, making it more difficult for you to use other subclasses of Application
(e.g., GreenDroid requires one IIRC). Using a static data member avoids that. However, do use the Application
Context
when instantiating this static SQLiteOpenHelper
(constructor parameter), so you do not leak some other Context
.
And, in cases where you aren't dealing with multiple threads, you can avoid any possible memory leak issues by just using one SQLiteOpenHelper
instance per component. However, in practice, you should be dealing with multiple threads (e.g., a Loader
), so this recommendation is only relevant for trivial applications, such as those found in some books... :-)
close()
, unless you happen to be in a circumstance where you're 100% sure it is safe to call close()
. –
Who SQLiteOpenHelper
as needed. If you happen to be in a position to close the SQLiteOpenHelper
, that will close the underlying SQLiteDatabase
. SQLiteOpenHelper
manages a single instance of SQLiteDatabase
, and the thread safety logic is in SQLiteDatabase
. –
Who SQLiteOpenHelper
, you will be using it to get at your SQLiteDatabase
. Close the helper, and it closes the database. –
Who SQLiteAssetHelper
and have no code to update it), something will be writing to the database. After all, you don't need write-ahead logging if you are not writing. Beyond that, I have no idea what to expect with multiple threads reading from separate SQLiteDatabase
instances backed by the same database file, as that is a niche scenario. You may wish to ask a separate Stack Overflow question on this. –
Who close()
. SQLite is transactional; all buffers are flushed and stuff written to disk by the time a transaction ends. There are no unflushed buffers or something that would cause data loss. "Will the resource be properly cleaned up when/if application is killed by OS at some point?" -- well, the process goes away, if that is what you mean by "cleaned up". –
Who Click here to see my blog post on this subject.
CommonsWare is right on (as usual). Expanding on his post, here is some sample code that illustrates three possible approaches. These will allow access to the database throughout the application.
Approach #1: subclassing `Application`
If you know your application won't be very complicated (i.e. if you know you'll only end up having one subclass of Application
), then you can create a subclass of Application
and have your main Activity extend it. This ensures that one instance of the database is running throughout the Application's entire life cycle.
public class MainApplication extends Application {
/**
* see NotePad tutorial for an example implementation of DataDbAdapter
*/
private static DataDbAdapter mDbHelper;
/**
* Called when the application is starting, before any other
* application objects have been created. Implementations
* should be as quick as possible...
*/
@Override
public void onCreate() {
super.onCreate();
mDbHelper = new DataDbAdapter(this);
mDbHelper.open();
}
public static DataDbAdapter getDatabaseHelper() {
return mDbHelper;
}
}
Approach #2: have `SQLiteOpenHelper` be a static data member
This isn't the complete implementation, but it should give you a good idea on how to go about designing the DatabaseHelper
class correctly. The static factory method ensures that there exists only one DatabaseHelper instance at any time.
/**
* create custom DatabaseHelper class that extends SQLiteOpenHelper
*/
public class DatabaseHelper extends SQLiteOpenHelper {
private static DatabaseHelper mInstance = null;
private static final String DATABASE_NAME = "databaseName";
private static final String DATABASE_TABLE = "tableName";
private static final int DATABASE_VERSION = 1;
private Context mCxt;
public static DatabaseHelper getInstance(Context ctx) {
/**
* use the application context as suggested by CommonsWare.
* this will ensure that you dont accidentally leak an Activitys
* context (see this article for more information:
* http://developer.android.com/resources/articles/avoiding-memory-leaks.html)
*/
if (mInstance == null) {
mInstance = new DatabaseHelper(ctx.getApplicationContext());
}
return mInstance;
}
/**
* constructor should be private to prevent direct instantiation.
* make call to static factory method "getInstance()" instead.
*/
private DatabaseHelper(Context ctx) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.mCtx = ctx;
}
}
Approach #3: abstract the SQLite database with a `ContentProvider`
This is the approach I would suggest. For one, the new LoaderManager
class relies heavily on ContentProviders, so if you want an Activity or Fragment to implement LoaderManager.LoaderCallbacks<Cursor>
(which I suggest you take advantage of, it is magical!), you'll need to implement a ContentProvider
for your application. Further, you don't need to worry about making a Singleton database helper with ContentProviders. Simply call getContentResolver()
from the Activity and the system will take care of everything for you (in other words, there is no need for designing a Singleton pattern to prevent multiple instances from being created).
Hope that helps!
LoaderManager.LoaderCallbacks
interface and Loader
s when working directly with a SQLite database instead of a ContentProvider. github.com/commonsguy/cwac-loaderex –
Gabble onTerminate()
, since the method might not even be called. including it seems safer though :) –
Embellishment SQLiteOpenHelper
, which is a helper class to manage database creation and version management. Most tutorials/articles/apps use it because it's easier than opening/closing/managing a raw SQLiteDatabase
on your own. –
Embellishment onTerminate()
will never be called in production environments, so overriding onTerminate()
will have no effect. –
Embellishment onTerminate()
there. –
Pocahontas private static DatabaseHelper mInstance
. Is there a way to avoid this issue? –
Terrel Having a single SQLiteOpenHelper
instance can help in threading cases. Since all threads would share the common SQLiteDatabase
, synchronization of operations is provided.
However, I wouldn't make a subclass of Application
. Just have a static data member that is your SQLiteOpenHelper
. Both approaches give you something accessible from anywhere. However, there is only one subclass of Application
, making it more difficult for you to use other subclasses of Application
(e.g., GreenDroid requires one IIRC). Using a static data member avoids that. However, do use the Application
Context
when instantiating this static SQLiteOpenHelper
(constructor parameter), so you do not leak some other Context
.
And, in cases where you aren't dealing with multiple threads, you can avoid any possible memory leak issues by just using one SQLiteOpenHelper
instance per component. However, in practice, you should be dealing with multiple threads (e.g., a Loader
), so this recommendation is only relevant for trivial applications, such as those found in some books... :-)
close()
, unless you happen to be in a circumstance where you're 100% sure it is safe to call close()
. –
Who SQLiteOpenHelper
as needed. If you happen to be in a position to close the SQLiteOpenHelper
, that will close the underlying SQLiteDatabase
. SQLiteOpenHelper
manages a single instance of SQLiteDatabase
, and the thread safety logic is in SQLiteDatabase
. –
Who SQLiteOpenHelper
, you will be using it to get at your SQLiteDatabase
. Close the helper, and it closes the database. –
Who SQLiteAssetHelper
and have no code to update it), something will be writing to the database. After all, you don't need write-ahead logging if you are not writing. Beyond that, I have no idea what to expect with multiple threads reading from separate SQLiteDatabase
instances backed by the same database file, as that is a niche scenario. You may wish to ask a separate Stack Overflow question on this. –
Who close()
. SQLite is transactional; all buffers are flushed and stuff written to disk by the time a transaction ends. There are no unflushed buffers or something that would cause data loss. "Will the resource be properly cleaned up when/if application is killed by OS at some point?" -- well, the process goes away, if that is what you mean by "cleaned up". –
Who I have written MultiThreadSQLiteOpenHelper which is an enhanced SQLiteOpenHelper for Android applications where several threads might open and close the same sqlite database.
Instead of calling close method, threads ask for closing the database, preventing a thread from performing a query on a closed database.
If each thread asked for closing, then a close is actually performed. Each activity or thread (ui-thread and user-threads) performs an open call on database when resuming, and asks for closing the database when pausing or finishing.
Source code and samples available here: https://github.com/d4rxh4wx/MultiThreadSQLiteOpenHelper
I did a lot of research on this topic and I agree with all the points mentioned by commonware . But i think there is an important point everyone is missing here , Answer to this question is entirely dependent on your Use Case so if your application is reading databases via multiple threads and only reading using Singleton has a huge performance hit as all the functions are synchronized and are executed serially as there is a single connection to database Open source is great, by the way. You can dig right into the code and see what’s going on. From that and some testing, I’ve learned the following are true:
Sqlite takes care of the file level locking. Many threads can read, one can write. The locks prevent more than one writing.
Android implements some java locking in SQLiteDatabase to help keep things straight.
If you go crazy and hammer the database from many threads, your database will (or should) not be corrupted.
If you try to write to the database from actual distinct connections at the same time, one will fail. It will not wait till the first is done and then write. It will simply not write your change. Worse, if you don’t call the right version of insert/update on the SQLiteDatabase, you won’t get an exception. You’ll just get a message in your LogCat, and that will be it.
The first problem, real, distinct connections. The great thing about open source code is you can dig right in and see what’s going on. The SQLiteOpenHelper class does some funny things. Although there is a method to get a read-only database connection as well as a read-write connection, under the hood, its always the same connection. Assuming there are no file write errors, even the read-only connection is really the single, read-write connection. Pretty funny. So, if you use one helper instance in your app, even from multiple threads, you never really using multiple connections.
Also, the SQLiteDatabase class, of which each helper has only one instance, implements java level locking on itself. So, when you’re actually executing database operations, all other db operations will be locked out. So, even if you have multiple threads doing stuff, if you’re doing it to maximize database performance, I have some bad news for you. No benefit.
Interesting Observations
If you turn off one writing thread, so only one thread is writing to the db, but another reading, and both have their own connections, the read performance shoots WAY up and I don’t see any lock issues. That’s something to pursue. I have not tried that with write batching yet.
If you are going to perform more than one update of any kind, wrap it in a transaction. It seems like the 50 updates I do in the transaction take the same amount of time as the 1 update outside of the transaction. My guess is that outside of the transaction calls, each update attempts to write the db changes to disk. Inside the transaction, the writes are done in one block, and the overhead of writing dwarfs the update logic itself.
Yes, that is the way you should go about it, having a helper class for the activities that need an instance of the Database.
© 2022 - 2024 — McMap. All rights reserved.