I have an app that use a sqlite database. For every query (10 query selects in 10 differents methods) i open the database. It is a good choice to open db when the application is started and close it when application close? For example, creating an static reference to my DB object in MyApplication class (extends Application).
Thanks for your advices.
Create a static singleton which keeps references to the db and other oft-used resources, such as this:
class Global {
private static SQLiteDatabase _db = null;
public static SQLiteDatabase getDb() {
if( _db == null ) {
... assign & open _db
}
return _db;
}
}
Elsewhere, just reference Global.getDb()
Related
In one of my Android applications,
- I have the SyncAdapter running in a different process, which actually queries the data from sqlite db and pushes the data to server.
- SqliteDatabase is singleton and there is only instance of it across the application.
But, right after I install the application, two SqliteDatabase instances are getting created. One for my application and one for the background process(sync). This way two instances are created and they are acting on the same db.
In these scenarios, if the two instances at a time try to insert the db, one of the request will throw error.
Is this the correct way to handle (CRUD db) when having multiple processes ?
Code where the single instance of db is created
public class SqliteStorage extends SqliteOpenHelper {
public SqliteStorage(Context context) {
super(context, DB_NAME, null, DB_VERSION);
db = getWritableDatabase();
db.setVersion(DB_VERSION);
}
public static synchronized SqliteStorage getInstance() {
if(mDBStorage == null) {
mDBStorage = new SqliteStorage(MyApplication.getContext());
}
return mDBStorage;
}
}
I am asking this question in context of a problem in my app, about which I find it difficult to create an exact question. But I do have a lead.
I do have parallel threads running and my problem revolves around the case where running queries on database returns NullpointerException on the initialized database instance.
So what I want to know is that if you initialize an instance of a database by db.getWritableDatabase() in 2 parallel threads, does closing the database in one thread by db.close(), closes it in the other thread ? infact across the application level ?
You should create singleton of SQLiteOpenHelper/db (you did not specify what class db is) which would return you only one instance and then you could check if db is closed or not.
I had similar problem and in the end 2 parallel threads and 1 database ? You are asking for problems.
You cannot safely have 2 actions operating with database at the same time.
/**
* Returns a writable database instance in order not to open and close many
* SQLiteDatabase objects simultaneously
*
* #return a writable instance to SQLiteDatabase
*/
public SQLiteDatabase getMyWritableDatabase() {
if ((db == null) || (!db.isOpen())) {
db = this.getWritableDatabase();
}
return db;
}
#Override
public void close() {
super.close();
if (db != null) {
db.close();
db = null;
}
}
Is it a good practice to open and close the database for every database transaction operation? let me clear you more.
I have two methods like
public SQLiteDatabase getDatabase() {
if (database == null || !database.isOpen()) {
database = getWritableDatabase();
}
return database;
}
public void closeDatabase() {
if (database != null && database.isOpen()) {
database.close();
}
}
so every time, when I am updating/inserting or deleting, I am opening the database and closing it.
public void insert(...) {
getDatabase().insert(...);
closeDatabase();
}
public void update(...) {
getDatabase().update(...);
closeDatabase();
}
public void delete(...) {
getDatabase().delete(...);
closeDatabase();
}
remember that all these methods are inside a class DatabaseHelper which is extending SQLiteOpenHelper and there is a global variable private SQLiteDatabase database
and I will perform these operations(insert/update/delete) more frequently.
So my question is Is it a good practice to open and close database for every transaction? if not, what is the good way? Where and When I have to close my database?
Opening and closing the database every time may (un-intentionally) run into problem such as Trying to open an already closed database.
Hence, I would suggest is to have a Singleton for the creating the database object, so that every time you make a call to database = getWritableDatabase(); you refer to the same object.
Consider closing this in onDestroy() method, so that as and when the App closes database is closed too.
private static AllItemsDB db; //AllItemsDB is my database class
public static AllItemsDB getDb() {
if (db == null) {
Log.d("","Issue here");
db = new AllItemsDB(app);
Log.d("","Issue here not");
}
return db;
}
since this is a static method, I can do AllItemsDB.myCRUD_methods and it will return me the same oblect every time and easy to access as well. :)
Help.
I have created a databaseprovider class which uses single instance of db object. Object is created in main activity and closed onDestroy method. This seems ok (but get some errors such as: db already closed or db is not open on some users devices that I cannot simulate).
I want to add a service to the application for the content download and this service can run with scheduler which make me think about single instance of db object will not work. Should I use another object for the service, will it result consistency problems? Can you kindly advice what would be the best way?
Databaseprovider class exm:
public class DatabaseProvider {
private static DatabaseHelper helperWriter;
public static SQLiteDatabase db_global;
public DatabaseProvider(Context c) {
helperWriter = DatabaseHelper.getHelper(c, true);
}
private static SQLiteDatabase getDB() {
if(db_global == null)
db_global = helperWriter.getWritableDatabase();
else if(!db_global.isOpen()) {
try {
db_global.close();
}
catch(Exception ex) {
ex.printStackTrace();
}
db_global = helperWriter.getWritableDatabase();
}
return db_global;
}
public String GetVersion() {
SQLiteDatabase db = getDB();
Cursor c = db.query(DatabaseHelper.PARAMETER_TABLE_NAME, new String[] {"VALUE"}, "KEY='Version'", null, null,null,null);
String version = "";
if(c.moveToNext())
{
version = c.getString(0);
}
else
version = "0";
c.close();
return version;
}
public long UpdateVersion(String value) {
ContentValues initialValues = new ContentValues();
initialValues.put(DatabaseHelper.PARAMETER_COLUMN_VALUE, value);
SQLiteDatabase db = getDB();
long r = db.update(DatabaseHelper.PARAMETER_TABLE_NAME, initialValues, "KEY='Version'", null);
if(r <= 0)
r = helperWriter.AddParameter(db, "Version", value);
//db.close();
return r;
}
public void CloseDB() {
if (db_global != null)
db_global.close();
db_global = null;
helperWriter.close();
}
}
Not sure if this will help, but...
you can't rely on onDestroy() in case the app crashes. Android may also keep your app in RAM, even if you exit it. Also, your main activity may get destroyed while the app is getting used if you are on a subactivity. It can also get recreated.
Sometimes it's better to have calls that open the DB, does stuff to it, and then closes it within the same function. If you are using a service, it may actually help things. I also am not sure if you should have a situation where a DB can be opened and/or accessed from a variety to different places at once without some management code
I see a couple questions:
A)
(but get some errors such as: db already closed or db is not open on some users devices that I cannot simulate).
...
Start an activity, then update content and some db operations in AsyncTask. While update is in progress go back and start the same activity again.
To work around these errors have you considered using a [Loader][1]? It's a callback based framework around ContentProviders.
B)
add a service to the application for the content download and this service can run with scheduler which make me think about single instance of db object will not work. Should I use another object for the service, will it result consistency problems?
This post by #commonsware from this website, suggests not to use Service for long running tasks. Instead the AlarmManager is suggested. I've only worked with short running services (for audio IO) myself.
We are using AsyncTasks to access database tables and cursors.
Unfortunately we are seeing occasional exceptions regarding the database being locked.
E/SQLiteOpenHelper(15963): Couldn't open iviewnews.db for writing (will try read-only):
E/SQLiteOpenHelper(15963): android.database.sqlite.SQLiteException: database is locked
E/SQLiteOpenHelper(15963): at android.database.sqlite.SQLiteDatabase.native_setLocale(Native Method)
E/SQLiteOpenHelper(15963): at android.database.sqlite.SQLiteDatabase.setLocale(SQLiteDatabase.java:1637)
E/SQLiteOpenHelper(15963): at android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1587)
E/SQLiteOpenHelper(15963): at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:638)
E/SQLiteOpenHelper(15963): at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:659)
E/SQLiteOpenHelper(15963): at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:652)
E/SQLiteOpenHelper(15963): at android.app.ApplicationContext.openOrCreateDatabase(ApplicationContext.java:482)
E/SQLiteOpenHelper(15963): at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:193)
E/SQLiteOpenHelper(15963): at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:98)
E/SQLiteOpenHelper(15963): at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:158)
E/SQLiteOpenHelper(15963): at com.iview.android.widget.IViewNewsTopStoryWidget.initData(IViewNewsTopStoryWidget.java:73)
E/SQLiteOpenHelper(15963): at com.iview.android.widget.IViewNewsTopStoryWidget.updateNewsWidgets(IViewNewsTopStoryWidget.java:121)
E/SQLiteOpenHelper(15963): at com.iview.android.async.GetNewsTask.doInBackground(GetNewsTask.java:338)
E/SQLiteOpenHelper(15963): at com.iview.android.async.GetNewsTask.doInBackground(GetNewsTask.java:1)
E/SQLiteOpenHelper(15963): at android.os.AsyncTask$2.call(AsyncTask.java:185)
E/SQLiteOpenHelper(15963): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:256)
E/SQLiteOpenHelper(15963): at java.util.concurrent.FutureTask.run(FutureTask.java:122)
E/SQLiteOpenHelper(15963): at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:648)
E/SQLiteOpenHelper(15963): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:673)
E/SQLiteOpenHelper(15963): at java.lang.Thread.run(Thread.java:1060)
Does anybody have a general example for code which writes to a database from a different thread than the one reading and how can we ensure thread safety.
One suggestion I've had is to use a ContentProvider, as this would handle the access of the database from multiple threads. I am going to look at this, but is this the recommended method of handling such a problem? It seems rather heavyweight considering we're talking about in front or behind.
We used a ContentProvider in the end. This appeared to clear up the problems.
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
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();
}
});
}
}
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.
We could not share Db connection with multiple thread to perform read and write operation in database simultaniously.We will have to make single object of DB using syncronization concept and we will perform one task at a time .We will use singleton pattern to make the DB object and it will be share within multiple threads.At a time will perform single task . then we will start other task or any operation on DB .
Content provider is not the solution of DB locking issue .
import java.util.concurrent.atomic.AtomicInteger;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DatabaseManager {
private AtomicInteger mOpenCounter = new AtomicInteger();
private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;
//private static String DB_PATH = "";
// private static String DB_NAME = "xyz.db";// Database name
private static String dbPathh;
public static synchronized void initializeInstance(SQLiteOpenHelper helper,
String dbPath) {
if (instance == null) {
instance = new DatabaseManager();
mDatabaseHelper = helper;
dbPathh=dbPath;
}
}
public static synchronized DatabaseManager getInstance() {
if (instance == null) {
throw new IllegalStateException(DatabaseManager.class.getSimpleName() +
" is not initialized, call initializeInstance(..) method first.");
}
return instance;
}
public synchronized SQLiteDatabase openDatabase(String thread) {
if(mOpenCounter.get() == 0) {
// Opening new database
// mDatabase = mDatabaseHelper.getWritableDatabase();
MyLog.e("Path Of DataBase", dbPathh);
// mDatabase=mDatabaseHelper.getWritableDatabase();
mOpenCounter.incrementAndGet();
mDatabase=SQLiteDatabase.openDatabase(dbPathh, null,
SQLiteDatabase. CREATE_IF_NECESSARY|SQLiteDatabase.OPEN_READWRITE);
MyLog.e("Open Data Base", " New Connection created" +thread);
}
else{
MyLog.e("Open Data Base", " Old Connection given " +thread);
}
// Toast.makeText(NNacres.getConfig(), "open conn: present connection =
" +mOpenCounter.get(), Toast.LENGTH_LONG).show();
return mDatabase;
}
public synchronized void closeDatabase() {
MyLog.e("Close db connection", ""+mOpenCounter.get());
if(mOpenCounter.get() == 1) {
// Closing database
mDatabase.close();
mOpenCounter.decrementAndGet();
Log.e("DB CLOSED", "DONE");
}
//Toast.makeText(NNacres.getConfig(), "close conn: after close =
" +mOpenCounter.get(), Toast.LENGTH_LONG).show();
}
}
and write this method in your YourSQLiteDataABse helper class which extends SQLiteOpenHelper Class
public SQLiteDatabase getWritableDatabase() {
DatabaseManager.initializeInstance(this,"data/data/your packgae name/databases/xyz");
return DatabaseManager.getInstance().openDatabase(getClass().getSimpleName());
}
public static String getMyDbPath(String DB_NAME, Context context) {
String myDbPath = context.getDatabasePath(DB_NAME).getPath();
MyLog.e("DB Path: "+myDbPath);
return myDbPath;
}
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.
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.