Sharing database connection - android

I just need some validation that this is a workable way to share a SQLlite database connection across my Activities and Services of my app, by using a helper class:
public class DatabaseHelper
{
private static DBItems dbItems;
private static SQLiteDatabase sdbItemsRead, sbItemsWrite;
public static synchronized DBItems getHelper(Context context)
{
if (dbItems == null)
dbItems = new DBItems(context);
return dbItems;
}
public static synchronized SQLiteDatabase select(Context context)
{
if (sdbItemsRead == null)
sdbItemsRead = getHelper(context).getReadableDatabase();
return sdbItemsRead;
}
public static synchronized SQLiteDatabase write(Context context)
{
if (sbItemsWrite == null)
sbItemsWrite = getHelper(context).getWritableDatabase();
return sbItemsWrite;
}
}
In my code I'm calling this to select data:
DatabaseHelper.select(this).rawQuery("SELECT * FROM TABLE");
and this to write data:
DatabaseHelper.write(this).update("tbl_items", cv, "[id] = ?", new String[] { itemId.toString() });
I need to do this because I'm calling the database from different threads (UI and background) and I was getting "database is locked" exceptions thrown.

If you are creating database and calling in some activities then it will work fine, but if you want that your database should run along with service then it is always better to write the database in custom Content Provider.
Custom Content Provider will help you to run your database in background along with service.
you can refer the custom content provider from this link.

Related

Access SQLite database simultaneously from different threads

Here is my problem. My app starts several threads, each for a particular object to be updated. The update of the object happens with a query to a single database. There is a single database and a single OpenHelper. The behavior of my app suggests me that the calls to the database are non simultaneous as well as I would like. How can I access the same database from different threads simultaneously? If the data for each object are in different tables is more efficient to split the database in several databases, one for each object?
public class SomethingToBeUpdated implements Runnable {
private SQLiteDatabase db;
#Override
public void run() {
db.rawQuery( ... bla bla
}
}
public class MainActivity extends Activity {
private SomethingToBeUpdated[] list = bla bla...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
for( SomethingToBeUpdated x : list ) {
new Thread(x).start();
}
}
}
For the sake of accessing the database in various threads you need to have a Database manager which keeps an object of your database class and pass it to any thread that needs it. In android you cannot access database simultaneously in several threads with different objects. It may just block your UI (the problem i was facing a few days ago).
So to overcome this problem you can use the the database manager i used which is defined as follows:
public class DatabaseManager {
private AtomicInteger mOpenCounter = new AtomicInteger();
private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;
public static synchronized void initializeInstance(SQLiteOpenHelper helper) {
if (instance == null) {
instance = new DatabaseManager();
mDatabaseHelper = helper;
}
}
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() {
if(mOpenCounter.incrementAndGet() == 1) {
mDatabase = mDatabaseHelper.getWritableDatabase();
}
return mDatabase;
}
public synchronized void closeDatabase() {
if(mOpenCounter.decrementAndGet() == 0) {
mDatabase.close();
}
}
}
Then you initialize it once like this:
DatabaseManager.initializeInstance(new ChatSQL(c));
And then you can get the database object wherever you want with this syntax:
SQLiteDatabase db = DatabaseManager.getInstance().openDatabase(); //in your methods which are querying the database
With this method your database is now thread safe. Hope this helps.
If the data for each object are in different tables is more efficient to split the database in several databases, one for each object?
No it is not efficient. It has a lot of overheads to define, access, make object and query different databases. And what if you want to join tables? you just cannot.

Attempt to reopen an already-closed object

I have one database helper class and three datasource classes for three tables in same database.
The database is accessed in lots of places via AsyncTasks. I faced this "attempt to reopen an already-closed object..." problem, I searched around and found that dbhelper.getReadableDatabase() returns same object for already opened connection. I guessed that problem must be due to when two threads performing operations simultaneously and one of them finishes its task and calls close() the connection gets closed and running thread throws this exception.
So to avoid close() I wrote following two methods:
public static synchronized void newOpenRequest() {
requestsOpen++;
Util.debuglog(TAG, "Open requests: " + requestsOpen);
}
public static synchronized boolean canClose() {
requestsOpen--;
Util.debuglog(TAG, "Open requests: " + requestsOpen);
if(requestsOpen == 0)
return true;
return false;
}
In all of three datasource classes, when I do it in following manner:
private void openRead() {
database = dbhelper.getReadableDatabase();
DBHelper.newOpenRequest();
Log.i(TAG, "Database opened.");
}
private void openWrite() {
database = dbhelper.getWritableDatabase();
DBHelper.newOpenRequest();
Log.i(TAG, "Database opened.");
}
private void close() {
if (DBHelper.canClose()) {
dbhelper.close();
Util.debuglog(TAG, "Database closed.");
}
}
My LogCat output is as follows:
So as highlighted in black rectangle, total openRequests were 0, so database closed, normal but as highlighted in red rectangle,
firstly openRequests were 0, so that time only database was supposed to get closed, but (my guess) what happened is canClose() returned true for a thread, and just before call to dbhelper.close(); another thread called open() (since openRequests = 1 is on LogCat just before close) and then first thread's close() invoked giving trouble to another running thread.
So looking for solution to avoid this concurrent access problem.
Thank you.
I have learned to never close the database in android. So maybe your fix is to not close the db. There is no point, keep it open during the entire life of your app. Android will release the resource when your app id destroyed.
You don't need to synchronize your database calls as sqlite can be thread safe.
Is Sqlite Database instance thread safe
DBOpenHelper works just fine:
public class DBOpenHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 31;
private static DBOpenHelper mInstance;
private static final String DATABASE_NAME = "thedb.db";
public static DBOpenHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new DBOpenHelper(context.getApplicationContext());
}
return mInstance;
}
private DBOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
sample using the db helper - close the cursor but not the db
SQLiteDatabase db = DBOpenHelper.getInstance(context).getWritableDatabase();
Cursor cursor = null;
try {
cursor = db.query...
}
finally {
cursor.close();
}

correct use of SQLiteOpenHelper (and some consultation)

I have the following code below.
I am creating a database in my application that uses SQLiteOpenHelper.
I have couple of concerns and would appreciate some consults.
Direct answers for these were not found on stack overflow as they might be subjective.
1 - I will be using this database from several activities. However I am not planning on making this a singleton to avoid leaks, but rather I will be getting the getWritableDatabase() and getReadableDatabase() inside each method. I plan on doing a db.close() inside each activity's onDestroy() .Is this advisable ? given my app has couple of activites and is not a huge app.
2 - I am not following and DAO model, nor I am using a different class for every table.
The way I see it, I don't need to. Do I ?
3 - (A question rather than consult)
In the code below, I am not creating a database of the form
private SQLiteDatabase database;
So all the references to the database (from my activities) are being done via the methods in the same subclassed SQLiteOpenHelper, therefore I am referencing the physically created database directly via getWritableDatabase and getReadableDatabase.
Do I need to create an instance of SQLiteDatabase and use it ? Even inside the subclass of SQLiteOpenHelper ?
Below is the code.
public class DbHelper extends SQLiteOpenHelper
{
private static final String DATABASE_NAME = "myDbName";
private static final String DATABASE_TABLE = "myTable";
private static final int DATABASE_VERSION = 1;
private Context ctx;
public DbHelper(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.ctx = context;
}
#Override
public void onCreate(SQLiteDatabase db)
{
db.execSQL("CREATE TABLE myTable(_id INTEGER PRIMARY KEY, title TEXT);");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
//nothing here now. maybe later.
}
public void insertTitle(String title)
{
ContentValues titleCV = new ContentValues();
titleCV .put("title", title);
getWritableDatabase().insert(DATABASE_TABLE, null, titleCV );
}
public void getTitles()
{
Cursor result = getReadableDatabase().rawQuery("SELECT _id, title FROM myTable", null);
while (result.moveToNext())
{
int id = result.getInt(0);
String titleGotten= result.getString(1);
}
result.close();
}
Q1
If you have a scenario within your app that have two parallel threads accessing the database, use a single instance of the SQLiteOpenHelper (singleton or member in the Application or whatever). If not you don't need to.
about calling db.close(), if it is in the onDestroy(), then it's fine.
Q2
a DAO is an abstraction layer to ease maintaining and scaling your project. If you are not going to scale or maintain your code (upcoming releases or something), then I suppose you don't need one.
Q3
You don't need to create an instance of SQLiteDatabse. when you call getReadableDatabase() or getWritableDatabase(), SQLiteOpenHelper creates and maintains an instance. The same instance is used the next time you call getReadable\WritableDatabase().
let me know if you still have questions.
step 1: make a staic instace of SqliteOpenHelper
step 2: you never close conexion to database, sqlite manage itself the sequencial access to write or read :)
private static ControladorBBDD instancia;
my class: public class ControladorBBDD extends SQLiteOpenHelper {
default :
private ControladorBBDD(Context ctx_p) throws Exception {
super(ctx_p, DB_NAME, null, DATABASE_VERSION);
try {
ControladorBBDD.ctx = ctx_p;
DB_PATH = ctx.getDatabasePath(DB_NAME).getAbsolutePath();
String myPath = DB_PATH;// + DB_NAME;
this.createDataBase();
db = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.OPEN_READWRITE);
} catch (SQLiteException ex) {
Conexiones.escribirLog(Log.getStackTraceString(ex),
ctx.getString(R.string.versionReal));
db.close();
}
}
and my way to implement a conexion to database:
public static synchronized ControladorBBDD getBBDD(Context ctx_p)
throws Exception {
if (instancia == null) {
instancia = new ControladorBBDD(ctx_p);
}
return instancia;
}
and to call it from activities:
dblectura = ControladorBBDD.getBBDD(getApplicationContext());
where private ControladorBBDD dblectura;
i hope that it helps, important thing is that you use applicationContext, no Activity context ;))
well if i were u i would create a class and the dbhelper as a subclass then i would use a open and a close function for main class and also the insert
whenever i want to use database i do it like this
mainclass mc=new mainclass(this);
mc.open();
mc.insert();
mc.close();

DBManager in Android application

I am using a DBManager layer, it holds as private member all all the SQLiteOpenHelpers for the tables. The class is as followed
public class DBManager
{
private static final String mTAG = "DBManager";
Context mContext = null;
DB1 mDB1 = null;
DB2 mDB2 = null;
public DBManager( Context context )
{
mContext = context;
mDB1 = new DB1( mContext );
mDB2 = new DB2( mContext );
}
#Override
protected void finalize() throws Throwable
{
Close();
super.finalize();
}
public void Close()
{
if( mDB1 != null ) mDB1.close();
if( mDB2 != null ) mDB2.close();
}
.... Public API towards the DB1/DB2....
}
The question is like this:
Currently I am using it in each activity I need the DB as a private member.
Maybe better to use it as singleton? Can I? If do - which context to pass?
Or any other way to use?
Thanks
Here's what I do. Keep one instance of SqliteOpenHelper per-database. Do not keep more than one Helper for a single database. This can cause issues with writes if more than one thread are trying to write at the same time.
Keep the Helper instance as a singleton across your application process. Multiple Activity and Service clients can access it easily and you won't have write lock issues.
See here for details:
http://www.touchlab.co/blog/single-sqlite-connection/
It links to a few blog posts of mine about Sqlite and multiple connections.

android SQLiteOpenHelper and Leaks

I wanna make SQLiteOpenHelper with some additional methods (like getStreetsCursor) which returns data from my db. So i wrote something like this:
public class DBHelper extends SQLiteOpenHelper {
public static final String DB_NAME="some.db";
public static final String T1_NAME="streets";
public static final String T1_FNAME1="name";
public static final String T2_NAME="addresses";
public static final String T2_FNAME1="name";
public static final String T2_FNAME2="address";
private Context appContext;
public DBHelper(Context context) {
super(context, DB_NAME, null, 1);
appContext=context;
}
public Cursor getStreetsCursor(String chars) {
SQLiteDatabase dbReadable=this.getReadableDatabase();
Cursor curStreets = dbReadable.query(DBHelper.T1_NAME,
new String[] {"_id",DBHelper.T1_FNAME1},
DBHelper.T1_FNAME1+" LIKE(\""+chars.toUpperCase()+"%\")",
null, null, null, DBHelper.T1_FNAME1);
return curStreets;
}
There are several methods like getStreetsCursor (getAddresses, getAddress4 etc) defined in DBHelper.
I guess if it is a DB Helper it definitely should have such methods i mean the DBHelper is a logical placeholder for them.
What i do in the activity is create a new DBHelper instance and store it in private field (called mDBHelper) of activity. Additionally in onDestroy method of activity i have mDBHelper.close().
private DBHelper mDBHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDBHelper = new DBHelper(this);
...
#Override
protected void onDestroy() {
if (mDBHelper!=null){
mDBHelper.close();
Log.i(APP_TAG,"mDBHelper.close() in "+this.getClass());
}
super.onDestroy();
}
Those activities uses the mDBHelper only in one way - by calling it's custom methods like mDBHelper.getStreetsCursor().
Eventually I found an exception message in logcat about my app got leaks in such activities which uses DBHelper. It says something like "database was never closed". So i decided to add a call to close() method in each of my custom methods just before return. So it looks like:
public Cursor getStreetsCursor(String chars) {
SQLiteDatabase dbReadable=this.getReadableDatabase();
Cursor curStreets = dbReadable.query(DBHelper.T1_NAME,
new String[] {"_id",DBHelper.T1_FNAME1},
DBHelper.T1_FNAME1+" LIKE(\""+chars.toUpperCase()+"%\")",
null, null, null, DBHelper.T1_FNAME1);
dbReadable.close();
return curStreets;
}
Now i got no leaks but got the next problem - only first call to mDBHelper.getStreetsCursor() really executes. All of the next calls returns null. Thats due to dbReadable.close(); line. If i remove it everythig works fine but i got leaks again.
So i cant figure out whats going wrong. In every custom method i got SQLiteDatabase dbReadable=this.getReadableDatabase(); line which should return a readable instance but after executing the close() method it doesn't.
I guess its about my custom methods because they calls .getReadableDatabase() inside an instance of DBHelper. If i place those methods directly in activity everything works fine - no leak exceptions and every time methods returns a proper data. But i want to place those methods in my DBHelper class.
So the main question is - what's wrong and how to do that properly?
You shouldn't have a SQLiteDatabase dbReadable=this.getReadableDatabase();
in every method getSomethingCursor as requesting a database object is expensive ( I think I read it in SO).
So you can create the object from your constructor
SQLiteDatabase dbReadable;
public DBHelper(Context context) {
super(context, DB_NAME, null, 1);
appContext=context;
dbReadable=this.getReadableDatabase()
}
public Cursor getStreetsCursor(String chars) {
Cursor curStreets = dbReadable.query(DBHelper.T1_NAME,
new String[] {"_id",DBHelper.T1_FNAME1},
DBHelper.T1_FNAME1+" LIKE(\""+chars.toUpperCase()+"%\")",
null, null, null, DBHelper.T1_FNAME1);
return curStreets;
}
Create a method to close the database handle:
public closeDb() {
if (dbReadable != null) { dbReadable.close();}
}
And in you activity:
#Override
protected void onDestroy() {
if (mDBHelper!=null){
mDBHelper.closeDb();
Log.i(APP_TAG,"mDBHelper.close() in "+this.getClass());
}
super.onDestroy();
}
And use startManagingCursor (if SDK < HoneyComb) to let your activity manage your cursor (closing it on onDestroy for example
Make SQLiteDatabase dbReadable a class member, and implement
public void close()
{
dbReadable.close();
}
Then, when you call mDBHelper.close(); in your activity's onDestroy() function, it should be ok.
Make a new function
public void DBclose()
{
curstreets.close();
dbReadable.close();
}
Then you can call this in your activity in onDestroy() fucntion.
Years after... I just want to state that now I'll better use Realm.io DB engine and I do it now in my projects. As for my old question I also want to notice that using getReadableDatabase() is a bad idea since across devices fragmentation I met some curios sqlite bug - readable instance will be closed automatically right after a query execution which is unexpected behavior. So I recommend always use getWritableDatabase() method even for reading purpose. And finally I end up with Dmytro Danylyk solution - give it a try, Dmytro is Google Developer Expert:
public class DatabaseManager {
private AtomicInteger mOpenCounter = new AtomicInteger();
private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;
public static synchronized void initializeInstance(SQLiteOpenHelper helper) {
if (instance == null) {
instance = new DatabaseManager();
mDatabaseHelper = helper;
}
}
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() {
if(mOpenCounter.incrementAndGet() == 1) {
// Opening new database
mDatabase = mDatabaseHelper.getWritableDatabase();
}
return mDatabase;
}
public synchronized void closeDatabase() {
if(mOpenCounter.decrementAndGet() == 0) {
// Closing database
mDatabase.close();
}
}
}
And use it as follows.
SQLiteDatabase database = DatabaseManager.getInstance().openDatabase();
database.insert(...);
// database.close(); Don't close it directly!
DatabaseManager.getInstance().closeDatabase(); // correct way

Categories

Resources