Managing database connections in an Android Activity - android

I have an application with a ListActivity that uses a CursorAdapter as its adapter. The ListActivity opens the database and does the querying for the CursorAdapter, which is all well and good, but I am having issues with figuring out when to close both the Cursor and the SQLiteDatabase.
The way things are handled right now, if the user finishes the activity, I close the database and the cursor. However, this still ends up with the DalvikVM warning me that I've left a database open - for example, if the user hits the "home" button (leaving the activity in the task's stack), rather than the "back" button.
If I close them during pause and then re-query during resume, then I don't get any errors, but then a user cannot return to the list without it requerying (and thus losing the user's place in the list). By this I mean, the user can click on any item in the list and open a new activity based on it, but will often want to hit "back" afterwards and return to the same place on the list. If I requery, then I cannot return the user back to the correct spot.
What is the proper way to handle this issue? I want the list to remain scrolled properly, but I don't want the VM to keep complaining about unclosed databases.
Edit: Here's a general outline of how I handle the code at the moment:
public class MyListActivity extends ListActivity {
private Cursor mCursor;
private CursorAdapter mAdapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAdapter = new MyCursorAdapter(this);
setListAdapter(mAdapter);
}
protected void onPause() {
super.onPause();
if (isFinishing()) {
mCursor.close();
}
}
protected void onDestroy() {
super.onDestroy();
mCursor.close();
}
private void updateQuery() {
// If we had a cursor open before, close it.
if (mCursor != null) {
mCursor.close();
}
MyDbHelper dbHelper = new MyDbHelper(this);
SQLiteDatabase db = dbHelper.getReadableDatabase();
mCursor = db.query(...);
mAdapter.changeCursor(mCursor);
db.close();
}
}
updateQuery() can be called multiple times because the user can filter the results via menu items (I left this part out of the code, as the problem still occurs even if the user does no filtering).
Again, the issue is that when I hit home I get leak errors. Yet, after going home, I can go back to the app and find my list again - cursor fully intact.

I usually have one SQLiteOpenHelper per app:
public class MyApp extends Application {
private static MyDbHelper dbHelper;
#Override
public void onCreate() {
super.onCreate();
dbHelper = new MyDbHelper(this);
}
#Override
public void onTerminate() {
super.onTerminate();
dbHelper.close();
}
public static SQLiteDatabase getDB() {
return dbHelper.getWritableDatabase();
}
}
Haven't had any problems with this approach so far.
And you can use startManagingCursor(..) available in Activity subclasses (though I let Adapters manage Cursors):
public class SomeAdapter extends CursorAdapter {
public void setNewFilter(CharSequense query){
// in fact, I use a DAO here
Cursor c = MyApp.getDB().query(..);
changeCursor(c);
}
}

I'd disagree. Looking at the android docs, specifcally this image http://developer.android.com/images/activity_lifecycle.png I would say the resource should be created in the onstart() method and cleaned up in the onstop() method. This is how google handles it in their code, google analytics docs for android

I would suggest using a Abstract Factory method to ensure that your SQLiteDatabase remains a singleton. Check out my blog post on the topic:
Correctly Managing Your SQLite Database

Close the Cursor and SQLiteDatabase in onDestroy(), for those things not already closed, and see if that helps. There may be scenarios where isFinishing() is false in onPause(), yet onDestroy() still winds up being called.

yeah usually
onDestroy()
is the where you should be closing your DB and open up it in the
onCreate()

Related

How to force serial/sequential execution of AsyncTasks in Android 2.x

There are a few Android APIs (after donut and before honeycomb) if Im not mistaken, where Google have enabled the AsyncTasks to run paralelly aiming for faster execution. Then lots of devs made mistakes when reaching out to the same database using multiple AsyncTasks, and since Android 3.0 AsyncTasks are running serially by default.
I am suffering this problem now when testing my app on an Android 2.3.4 device with my SQLite
First, Im getting categories from the server, I open DB, insert them close DB.
Second I get the subcategories from the server, open DB, insert them into DB, close DB
Third I get user items from the server, open DB, insert items, then close DB
Im taking good care to ensure that one starts after another, but in every 8-10 iterations something somewhere slows down and overlaps with another procedure right in the moment where a task is opening the db, another task closes it right after, and the first task starts trying to write to a closed db....
What do I do? I want clean, reliable separation, sequential execution and I dont want to start the asynctasks from the previous asynctask's onPostExecute, because these three will not always run in a row
I read an article yesterday that you CANT do it on android 2.x
Shall I try to open the DB and DBHelper before ALL of the operations and close the DB afterwards?
EDIT: Usually I get the error here (at Begin transaction):
(The error says that the DB is closed)
#Override
protected Void doInBackground(Void... arg0) {
// dbTools.close();
try {
if (database == null) {
database = dbTools.getWritableDatabase();
}
} catch (Exception e) {
e.printStackTrace();
}
ContentValues values = new ContentValues();
database.beginTransaction();
try {
// Iterating all UserItem objects from the LinkedHashSet and getting their info
for (UserItem userItem : userItems) {
// Inserting values for the database to insert in a new record
values.put(C.DBColumns.ITEM_ID, userItem.getItemId());
values.put(C.DBColumns.ITEM_NAME, userItem.getItemName());
// database.insertWithOnConflict(C.DBTables.ITEMS, null, values, SQLiteDatabase.CONFLICT_REPLACE);
database.insert(C.DBTables.ITEMS, null, values);
} // End of For loop
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
// Closing all cursors, databases and database helpers properly because not closing them can spring lots of trouble.
if (database != null && database.isOpen()) {
try {
database.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
} // End of doInBackground
And this is my DBTOOLS CLASS:
public class DBTools extends SQLiteOpenHelper {
// Its a good practice for DBTools to be a singleton. Do not instantiate it with "new DBTools(context)" but with
// DBTools.getInstance(context) instead
private static DBTools sInstance;
public static DBTools getInstance(Context context) {
if (sInstance == null) {
sInstance = new DBTools(context);
}
return sInstance;
}
public DBTools(Context context) {
super(context, C.Preferences.LOCAL_SQLITE_DATABASE_NAME, null, 1);
}
public void onCreate(SQLiteDatabase database) {
database.execSQL(SQLQueries.tableCategoriesCreate);
database.execSQL(SQLQueries.tableSubcategoriesCreate);
database.execSQL(SQLQueries.tableItemsCreate);
}
public void onOpen(SQLiteDatabase database) {
database.execSQL(SQLQueries.tableCategoriesCreate);
database.execSQL(SQLQueries.tableSubcategoriesCreate);
database.execSQL(SQLQueries.tableItemsCreate);
}
public void onUpgrade(SQLiteDatabase database, int version_old, int current_version) {
database.execSQL(SQLQueries.tableCategoriesDrop);
database.execSQL(SQLQueries.tableSubcategoriesDrop);
database.execSQL(SQLQueries.tableItemsDrop);
onCreate(database);
}
} // End of Class
Since you can't call from onPostExecute, I would say you have two options, one would be to move your open close calls to the beginning and end of your activity or service.
Option two would be to setup a reference counter in your DB and DBHelper where you track the number of times open has been called, and then decrement that count when close is called. That way you can perform close only when the count is 0. One thing to remember when taking this approach is that you should probably have a method that will force the db to close that you call when you are sure your other connections are done. This shouldn't be necessary but will be a failsafe to ensure the db gets closed if something goes wrong.
Edit: You would have to make DBTools a singleton for it to work, but it's not equivalent. Here's a quick example.
public class DBTools {
private static DBTools instance;
private static int openCount;
public DBTools getInstance() {
if (instance == null) {
instance = new DBTools();
}
return instance;
}
private DBTools() {
openCount = 0;
}
public void open() {
openCount++;
//Do open
}
public close() {
openCount--;
if (openCount == 0) {
//Do close
}
public void forceDBClose() {
//Do close
}
}
I am also a newbee in android. I was having a problem like this too.
To overcome this, i used Singleton class.
I created one instance of the DBHelper class and used it in all my asynctasks.
So, until the DB is closed, all the asynctasks access the initialised DB object.
If there is no object in the memory, the async tasks, instantiates it and use it then.

Android sqlite insert / multiple reads from many threads

I don't know how to handle this correctly without getting database locked errors.
My app basically downloads many items in batches of 100 rows (~ 60.000 items) and inserts them in the database. Each batch of 100 rows is processed into a transaction.
The main activity allows the user to navigate between screens (fragments) while records are being downloaded and inserted. Most of the other screens contains read data from the database. I get a lot of database lock errors during reading. All readings are done in the main activity (not fragments) in different async tasks
So far I just used the "classic approach"
public class DBAdapter {
public DBAdapter(Context ctx) {
this.context = ctx;
DBHelper = new DatabaseHelper(context);
}
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DB_CREATE_TABLES);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Utils.log("Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data");
onCreate(db);
}
public DBAdapter open() throws SQLException {
database = DBHelper.getWritableDatabase();
return this;
}
public void close() {
DBHelper.close();
}
Then on my activity's onCreate() I call db = new DBAdapter(context); and each time I am doing an database operation (read/write) I call db.open() and after the insert/read is done I call db.close()
My questions are:
what would be the best approach to this situation ?
Considering I do a lot of write/read would it be better to call db.open on onCreate and db.close() on onDestroy() ? Would this be better than calling open/close each time I need to access the database ?
What do I need to do to avoid database locking on reading ?
I had a exactly similar situation like yours. In addition to what you described, in my app the user also can update the database through input on the screen.
The way I resolved it ( I don't know if it's the best way, but I hardly see any locking issue now)
Make a singleton class derived from SQLiteOpenHelper to make sure only one instance is running at any given time.
Implement ContentProvider class for insert/update/delete/query operations. Make all those functions 'synchronized'
Only close the db in ContentProvider's shutdown function. I do a very frequent db operations, so I don't want to open/close everytime. But I am not sure if it's the correct way of handling it.
Do access DB only through ContentProvider interface from anywhere
A very simple approach, or maybe a workaround is using synchronized methods for opening and closing the database object. I don't really know if it's the best practice, but at least it's simple and easy. Add this methods to your DBAdapter Class, and use them instead of db.open and db.close. The use_count attribute simple holds how many times open has been called. Initialize it with a value of 0. Also, in order to make it work on your solution be sure to pass the same DBAdapter object between the fragments. Don't create a new one everytime :
private int use_count = 0;
public synchronized void doOpen()
{
use_count++;
this.open();
}
public synchronized void doClose()
{
use_count--;
if (use_count == 0)
{
this.close();
}
}
Consider wrapping the SQLite database in a ContentProvider and using CursorLoader to do the queries from the various activities & fragments. This isolates the management of the database from the Activity/Fragment life cycle and can result in many fewer open/close cycles.
You may still run into contention between the reads and writes, but having all the database interaction in the same module should make it easier for you to address these issues.
Some interesting links: http://www.vogella.com/articles/AndroidSQLite/article.html#todo
When to use a Content Provider

Android, How to manage opening/closing database connection when we have multiple tables?

I am looking for best approach to handling database connection. I have designed my application base on following method and works fine. However, sometimes application crashes especially on old model devices. Please look at my following method.
I have a DatabaseHelper class which all of tables will be created here. This class is like this:
public class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
Log.i(TAG, "Object created.");
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CRT_TBL_1);
db.execSQL(CRT_TBL_2);
db.execSQL(CRT_TBL_3);
db.execSQL(CRT_TBL_4);
db.execSQL(CRT_TBL_5);
db.execSQL(CRT_TBL_6);
db.execSQL(CRT_TBL_7);
db.execSQL(CRT_TBL_8);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(DatabaseHelper.class.getName(), "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data.");
onCreate(db);
}
}
As you have seen, I have 8 tables which provides data for 8 different activities. Implementation of handler class for one activity is like following code.
public class DatabaseHandler_1 {
private final String TAG = "DatabaseHandler_1 ";
private DatabaseHelper dbHelper;
private SQLiteDatabase database;
public DatabaseHandler_1 (Context context) {
dbHelper = new DatabaseHelper(context);
Log.i(TAG, "Object created.");
}
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
public void close() {
dbHelper.close();
}
// ... Other methods insert, update, query, ...
}
Finally, based on required information one or some of them will be used in each activity.
public class Activity_1 {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "Try to create activity...");
// Creating Database/Table
dbHandler_1 = new DatabaseHandler_1 (this);
dbHandler_1 .open();
dbHandler_2 = new DatabaseHandler_2 (this);
dbHandler_2 .open();
dbHandler_3 = new DatabaseHandler_3 (this);
dbHandler_3 .open();
}
#Override
protected void onDestroy() {
super.onDestroy();
// Closing database
dbRecipesHandler.close();
dbFavoritsHandler.close();
dbShoppingHandler.close();
}
// Other methods for displaying data...
}
When crashes happens logcat shows the connection is close while my application tries to query in database. I think scenario is like this:
User opens 1'st activity then he goes to second activity and then he goes to third activity. Now, if user clicks on back button third activity will be closed and onDestroy() method of this activity closes database connection. Because, second activity is still in memory, activity assumes that the connection is still open and when it wants to query into database, crash happens.
Logcat shows a message like this: Database connection is close. You wanted to query...
One solution is instead of letting Android OS handles database connection, we manually open connection and use it the manually close it.
Second solution is in Handler class manually open and close connection for each method.
What is you suggestion? did you have any experience about it?
Any suggestion would be appreciated.
In general I would suggest you consider wrapping your data with Content Providers then using Loaders in your activities to mingle data with the UI. It might seem like a lot of work, but among the many benefits of content providers you will not have to manage database connections or cursors within your activities. (Note, your DatabaseHelper class would remain the same)
Having done data both your way as well as with content providers in the different apps I've made, I would say content providers are almost always the way to go. It's always ended up being more maintainable and extendable in my experience, plus you get a lot of baked in features that would be really expensive to build yourself.
That said, if you really prefer the more direct approach you're currently using, I would at the least not tie the opening and closing of your connections to the activity's life cycle. Only open those connections at the instant you actually need data, get that data, then immediately close any cursors along with the connection itself.
Finally I solved the problem. I thought that I should open database one time in activity. I tried to open database again in onResume() method of each activity and closing it in onPause() of each activity instead of closing in onDestroy() method.
So far problem solved. Hope to don't see it again. If i face it again then will update/remove answer.

How to Open/Close SQLite db in Android Properly

I have an app that functions properly and does not force close or crash. But when I look at LogCat, it occasionally gives me this:
05-20 15:24:55.338: E/SQLiteDatabase(12707): close() was never explicitly called on database '/data/data/com.---.--/databases/debt.db'
05-20 15:24:55.338: E/SQLiteDatabase(12707): android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
a little ways down...
05-20 15:24:55.338: E/System(12707): Uncaught exception thrown by finalizer
05-20 15:24:55.338: E/System(12707): java.lang.IllegalStateException: Don't have database lock!
I am not sure when I should be opening and closing my Database?
I have a Main activity that is simply a splash screen. It then goes into an activity that calls a ListView using info from the DB; so it is at this activity where the DB is first opened.
There is also one other Activity where the DB is required that branches off the one with the ListVeew. When am I supposed to be opening and closing this? Word seems to be that I simply need to open once, and then close when the app is "paused", "stopped" or "destroyed".
If this is the case, where do I put the db.close() method... in the Splash Screen Main Activity where onStop, etc is located? or the same Activity as the one that opens the DB? or.. is there another place?
UPDATE:
This is the line in code that the error keeps pointing to:
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
If you're using an instance of a DatabaseHelper class, and after you initialize the DBHelper object, every time you do work in the database you should call the open method before you do work, then create a new cursor, query the database, do work with the information you just stored in the cursor, when you're done close the cursor, then close the database. For example if you wanted to grab every item in a database you would do something like :
...
DataBaseHelper db = new DataBaseHelper(this);
...
db.open();
Cursor cursor = db.getAllItems();
maxCount = cursor.getCount();
Random gen = new Random();
row = gen.nextInt(maxCount); // Generate random between 0 and max
if (cursor.moveToPosition(row)) {
String myString = cursor.getString(1); //here I want the second column
displayString(myString); //private method
}
cursor.close();
db.close();
getAllItems is a public method in my DatabaseHelper, it looks like this in case you were wondering
public Cursor getAllItems() {
return db.query(DATABASE_TABLE,
new String[] {
KEY_ROWID,
KEY_NAME
},
null,
null,
null,
null,
null);
}
This is how I access my database and I haven't gotten any of the errors you've got, and it works perfectly.
I used to do the way #Shikima mentioned above but in complex applications which has many background services, multi-threading,etc it can get real tiresome when you have to manage many database instances and on top of that, opening and closing them.
To overcome this, I used the following method and it seems to be working fine.
1.
Declare and initialize an instance of YourDBHelperClass in your Application base class like this :
public class App extends Application {
public static YourDBHelperClass db;
#Override
public void onCreate() {
super.onCreate();
db = new YourDBHelperClass(getApplicationContext());
db.open();
}
}
2.
In you activity, or any other place you want to use the DB, initialize the YourDBHelperClass object like this :
YourDBHelperClass db = App.db;
And then you can use the database anyway you want without having to worry about opening and closing it manually each time. The SQLiteOpenHelper takes care of the closing when the Application is destroyed
You are probably not handling your database correctly; you are opening more database instances than you are closing.
There are a number of design patterns you can follow to correct this behavior. You might want to consult this answer for more information.

accessing a DBadapter globally and Finalizing cursor android.database.sqlite ERROR

I have a DB that I use in all my activities. There is only one record in the DB.
In the first activity it is opened or created and then put in my globally used object like this
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// first get the current data from the DB
myDBAdapter = new MyDBAdapter(this);
GlobalVars.myDBAdapter = myDBAdapter; // we store the DBAdapter in our global var
myDBAdapter.open();
Cursor cursor = myDBAdapter.fetchMainEntry();
startManagingCursor(cursor);
// if there is no DB yet, lets just create one with default data
if (cursor.getCount() == 0) {
createData();
cursor = myDBAdapter.fetchMainEntry();
startManagingCursor(cursor);
}
Now in another activity I access the already open DB like this...
GlobalVars.myDBAdapter.updateMainEntry(1,.....);
I do not close the DB when leavin one activity to go to the next. The DB is just accessed (since it has been opened at the very first activity).
Only when leaving the app I clode the DB like this...
#Override
protected void onDestroy() {
super.onPause();
myDBAdapter.close();
}
The background why I am also asking this is I get this error...
Finalizing cursor android.database.sqlite.SQLiteCursor#48106730 on
mainEntry that has not been deactivated or closed
and it seems that my app crashes on certain devices - but I can't find the reason for it during debugging.
Is that correct and best practice, or do I have to close the DB when I leave the activity and open it when entering the next activity when switching between activities?
Many thanks!
The best thing (I have it tested in a few apps of mine) is to:
declare database adapter as an activity's instance variable:
private DBAdapter mDb;
create it and open in activity's onCreate():
mDb = new DBAdapter(this);
mDb.open();
close it in activity's onDestroy():
mDb.close();
mDb = null;
Works like charm.
A side note: the Application class onTerminate "will never be called on a production Android device" according to the docs.
You can use sqlite as DB for your application. Then you have to create a common class for your whole application Like " DBAdapter ". Then write codes to manipulate the DB. After that you just have to create DBAdapter's object in your activity. Thus you can access your DB from every activity of your app. http://developer.android.com/guide/topics/data/data-storage.html#db This link can be useful.

Categories

Resources