Keeping a global reference to SQLiteDatabase? - android

I'm using a single sqlite database throughout my app. So I want to wrap a connection to the db in a singleton for convenience. At first I thought I could keep a reference to the SQLiteDatabase around for that:
MySQLiteOpenHelper helper = new MySQLiteOpenHelper(appContext); // local
myGlobalSQLiteDatabase = helper.getWritableDatabase(); // global
...
void someFunction() {
try {
myGlobalSQLiteDatabase.insertOrThrow(...);
} catch (Exception ex) {
}
}
but this would result in errors such as:
(1802) os_unix.c:30011: (2) stat(/data/data/com.me.test/databases/test.db) -
(1802) statement aborts at 16: [INSERT INTO mytable(f1,f2) VALUES (?,?)]
android.database.sqlite.SQLiteDiskIOException: disk I/O error (code 1802)
at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:775)
...
(this is all being done on the main thread, a single test).
My second attempt was to instead keep a global reference to the helper only:
myGlobalSQLiteOpenHelper helper = new MySQLiteOpenHelper(appContext); // global
...
void someFunction() {
SQLiteDatabase db = myGlobalSQLiteOpenHelper.getWritableDatabase();
try {
db.insertOrThrow(...);
} catch (Exception ex) {
} finally {
db.close();
}
}
and that works. I have to call getWritableDatabase() and close() on each call of someFunction().
I don't know how much overhead there is in getWritableDatabase() and close(), I was originally hoping for the fastest implementation possible, as I'll be calling someFunction() repeatedly in response to user input. Is the second method the best option for this setup?
Thanks

You don't need to write getwritable database again and again just make a constructor of db class public DBCustomer open() throws SQLException
{
db = DBHelper.getWritableDatabase();
return this;
}
and call all the function of db just decalring object and calling object.open function

Related

SQLite - getDatabase called recursively

Here is my code:
public class DBHelper extends SQLiteOpenHelper {
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("sql-query");
try {
//db = getWritableDatabase();
fillAllDB(db);
} catch (JSONException e) {
e.printStackTrace();
}
}
// some code
}
fillAllDB(db); method adds a record to the table, but I can't use db = getWritableDatabase(); before method call - this is causing the looping. What to do in this case?
OnCreate method gives you writable database only, so no need to get the writable database, when you call the writable/readable database, sqlite framework checks whether database exist or not, if not then it will call the onCreate method, that why this is recursive, you should not call getWritable/getReadable database in onCreate/onUpgrade method of the SqliteOpenHelper class
Use the SQLiteDatabase db argument passed to your onCreate() method as the database to call your operations on.
getWritableDatabase() and getReadableDatabase() will invoke your onCreate() in case the database file did not exist, and calling get...Database() recursively while the previous call is still being processed causes this exception.
Also, catching exceptions in onCreate() is not a good idea. If there's a problem, the onCreate() method should not return normally - that tells the framework that the database setup was successful.

Can't get SQLite writable database

I'm trying to implement android SQLite usage design pattern that ensures one opened SQLiteDatabase instance per application.
public class BaseDataSource {
private static final CustomSQLiteHelper dbHelper = CustomSQLiteHelper.getInstance();
protected static SQLiteDatabase database;
static {
//HERE RISES EXCEPTION
BaseDataSource.database = BaseDataSource.dbHelper.getWritableDatabase();
}
private void close() {
if(null != BaseDataSource.database && BaseDataSource.database.isOpen()) {
BaseDataSource.database.close();
if(null != BaseDataSource.dbHelper) {
BaseDataSource.dbHelper.close();
}
}
}
protected BaseDataSource() {}
protected void finalize () throws Throwable {
close();
super.finalize();
}
}
But while my applications starts I get this kind exception:
Caused by: java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/data/com.xxx/databases/xxx.db
How SQLiteDatabse database can be opened and closed before class was created?
UPDATED
I found my own bug. It was in CustomSQLiteHelper class. In onCreate method I closed database. I tried every soliution that I found in internet and due to that I made a bug.
if you going to event any thing then first need to open as write database. So for that use this method
and call like this
openAsWrite();
public void openAsWrite() throws SQLException {
db = DBHelper.getWritableDatabase();
}
// ---closes the database---
public void close() throws SQLException {
DBHelper.close();
}
Use following pattern when getting a database object:
try {
if (sDatabase != null) {
if (!sDatabase.isOpen()) {
sDatabase = sContext.openOrCreateDatabase(DATABASE_NAME, 0, null);
}
} else {
// open database here
sDatabase = sContext.openOrCreateDatabase(DATABASE_NAME, 0, null);
}
Log.d(TAG, "Database successfully opened.");
} catch (SQLException e) {
Log.e(TAG, "" + e);
}
Using database object as static makes your class behave like this.
Creates database instance when the class is loaded.
closes the database connection when finalize method is called.
your class will not going to acquire database connection anymore, and it will have the database instance which is closed already.
eventually when try to access the instance which is closed, it pops the error to you
I think you should use a different approach will be better.

Android SQLite leaked Error

I've got my database connection setup in an Application but LogCat keep's telling me about a SQLite leak
04-25 11:22:23.771: W/SQLiteConnectionPool(9484): A SQLiteConnection object for database '+data+data+com_appstart+databases+database' was leaked! Please fix your application to end transactions in progress properly and to close the database when it is no longer needed.
I'm starting to wonder if it is down to how I'm using the database adapter.
I attach my code...
This is my code where some times i found and error..
try{
DBAdapter dba = new DBAdapter(this);
dba.open();
Cursor c = dba.getModules(Constant.LANGUAGE_ID);
for (int i = 0; i < c.getCount(); i++) {
if (i > 2) {
a.add(c.getString(2));
moduleid.add(c.getString(0));
icon_name.add(c.getString(1));
System.out.println(c.getString(2) + "------" + c.getString(0));
}
c.moveToNext();
}
c.close();
dba.close();
}catch(Exception e){
e.printStackTrace();
}
this is my DBAdapter class that contains Open() and Close() methods.
public DBAdapter open() throws SQLException {
db = DBHelper.getWritableDatabase();
return this;
}
public void close() {
DBHelper.close();
}
What is this DBAdapter class you're using? I don't know what it's doing so I don't know if it's correct. You should check where the SQLiteConnection object is obtained that the error message refers to, and ensure that that SQLiteConnection is close()d.
Are you getting the error only occasionally or all the time? It's probably not the main problem you are observing, but your code also fails to call close() when there is an exception before the close(). You should ensure close() gets called regardless of exception path by guarding it with a try/finally block:
dba.open();
try {
Cursor c = ...;
try {
...
} finally {
c.close();
}
} finally {
dba.close();
}
Your problem is that you have to "Close" the database once you finished your database operation. So close your DB wherever you opened your DB.
in DBAdapter class, in close function replace DBHelper.close() from db.close();
public void close() {
DBHelper.close();
}
replace with the below;
public void close() {
db.close();
}
public DBAdapter open() throws SQLException {
db = DBHelper.getWritableDatabase();
//You should close the opened database
DBhelper.close
return this;
the same also goes for cursor once you opened it it needs to be closed.

SQLite IllegalStateException: where do I close the database?

I'm getting two contradicting Exceptions when creating and populating my new SQLiteDatabase in Android. In short my code:
SQLiteOpenHelper extending class:
public void onCreate(SQLiteDatabase db) {
db.execSQL(DB_TABLE_CREATE);
loadLevelData(db); //puts data in the database
//db.close(); <<< ?
}
In my activity class I instantiate this class (in onCreate()), and call getWritableDatabase():
dbHelper = new DbOpenHelper(getApplicationContext());
database = dbHelper.getWritableDatabase();
Now if I don't call db.close() after populating the database, like above, I get
android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
However if I DO close it, I get the following exception:
java.lang.IllegalStateException: database not open
on getWritableDatabase().
This really confuses me, so could anyone help me with what's wrong?
You are not expected to close the database in the DatabaseHelper class. However you need to close it every time you open it calling getWritableDatabase:
dbHelper = new DbOpenHelper(getApplicationContext());
database = dbHelper.getWritableDatabase();
//... do something with database
database.close();
You are closing your database at the wrong time.
I typically keep the database around like this:
public class MyActivity extends Activity {
SQLiteDatabase writeableDb;
// ...
// Code
// ...
public void onStart(){
super.onCreate(savedState);
// Do stuff, get your helper, etc
writeableDb = helper.getWriteableDatabase();
}
public void onStop(){
writeableDb.close();
super.onStop();
}
}
Alternatively, wrap all your code working with that db connection in a try/finally block
db = helper.getWriteableDatabase();
try { // ... do stuff ... }
finally { db.close(); }
Note: All of the opening/closing should be done in the Activity working with the database, not the open helper.

Android threading and database locking

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.

Categories

Resources