Android SQLite database gets corrupted - android

This link describes my problem exactly: http://old.nabble.com/Android-database-corruption-td28044218.html#a28044218
There are about 300 people using my Android App right now and every once and while I get a crash report to the server with this stack trace:
android.database.sqlite.SQLiteDatabaseCorruptException: database disk image is malformed
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2596)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2621)
at android.app.ActivityThread.access$2200(ActivityThread.java:126)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1932)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4595)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
at dalvik.system.NativeStart.main(Native Method) Caused by: android.database.sqlite.SQLiteDatabaseCorruptException: database disk image is malformed
at android.database.sqlite.SQLiteQuery.native_fill_window(Native Method)
at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:75)
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:295)
at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:276)
at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:171)
at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:248)
The result is the app crashing and all the data in the DB being lost.
One thing to note is that every time I read or write to the database I get a new SQLiteDatabase and close it as soon as I'm done. I did this in an attempt to prevent these kind of corruption errors.
I also tried synchronizing all DB reads and writes using a single static object and that didn't seem to help.
Is it possible this is just a SQLite bug?
I found a similar bug with the built-in email app here: http://code.google.com/p/android/issues/detail?id=5610.
Here is my code:
public class KeyValueTableAdapter extends BaseTableAdapter {
private String tableName;
private String keyColumnName;
private String valueColumnName;
public KeyValueTableAdapter(Context context, String tableName, String keyColumnName, String valueColumnName) {
super(context);
this.tableName = tableName;
this.keyColumnName = keyColumnName;
this.valueColumnName = valueColumnName;
}
protected String getStringValue(int key) {
Cursor cursor = null;
SQLiteDatabase db = null;
String value;
try {
db = dbOpenHelper.getReadableDatabase();
cursor = db.query(true, tableName, new String[] { valueColumnName }, keyColumnName + "=" + key, null, null, null, null, null);
if ((cursor.getCount() == 0) || !cursor.moveToFirst()) {
value = null;
} else {
value = cursor.getString(0);
}
} finally {
if (cursor != null) cursor.close();
if (db != null) db.close();
dbOpenHelper.close();
}
return value;
}
}
public abstract class BaseTableAdapter {
protected DbOpenHelper dbOpenHelper;
public BaseTableAdapter(Context context) {
this.dbOpenHelper = new DbOpenHelper(context, DatabaseSettings.DATABASE_NAME, null, DatabaseSettings.DATABASE_VERSION);
}
}

Most likely the database process(es) is getting killed during an I/O. For example by a task killer, or if you're allowing db write operations to continue during a time when the app should shutdown or sleep...
See if you can reproduce the issue by putting your app in a DB write loop and using a task killer on it.
Scenario: 32 bytes being written to the database, the writer task gets killed after only writing 10, result: database left in inconsistent and possibly corrupt state.
Also see:
Android process killer
EDIT: opening and closing the DB for each read/write? stop that! :)

Using multiple instances of SQLiteDatabase could be causing your problem if you have two instances updating the same database file at the same time.

Implement a backup db process each day, and if the DB gets corrupted you just replace the database with a backup. You can use simple file copy paste methods to maintain a backup each day.

Since I can't comment -yet- on Brad's post.
I have to agree with the extra overhead.
I have a HTC Magic as my daily phone, and ram is always an issue.
Android phones are at very different ends of the $$$
Some are super cheap and some are super expensive, this basically comes down to ram, and cpu.
People who run task killers do ruin their android phones.
As a developer you should suggest people not to use them, or just deny support to people who use task killers, since android doesn't need these "improvements" (ask steve (cyanogen))
Also the new statement in android is very expensive.
You want to limit the amount of new calls when programming for android.
Programming for android is all about reuse of the precious memory. (HTC Magics/Dreams only have 96MB available to applications, and most of it is already in use)
As for your SQLiteDB...the API says that your SQLiteDB is private to your application.
I don't see why you need to open and close a NEW connection to it each time you want to read or write to it.
I would rather keep the connection open until the user looses focus from it.
However, if you are writing a content provider, that is a different story.

"the DB holds session information so
it's not very feasible to do a backup.
The data changes by the minute"
You should try using SharedPreferences: it stores key-value pairs (in the background, it uses a file).
Storing values:
SharedPreferences sp=MyActivity.getSharedPreferences("Name", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("key", value);
editor.putBoolean("another", true);
editor.commit();
Retrieving data:
sp.getString("key", "Not found");
// "Not found" is the default value
// if sp does not contain the specified key
sp.getBoolean("another", false);
// false is the default value
// if sp does not contain the specified key
See getSharedPreferences and SharedPreferences for a more detailed description.

Related

Attempt to write a readonly database... but I'm not

I have a read-only database connection. Sometimes, when reading data from the database with a SELECT query, it throws a SQLiteReadOnlyDatabaseException.
I open the connection like this:
return SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
The query is:
Select * FROM BudgetVersions WHERE entityId = ?
I read data from the database using db.rawQuery(), like this:
String query = ...;
Cursor c = db.rawQuery(query, new String[]{ activeBudgetId });
try {
if (c.moveToFirst()) {
bv.versionName = c.getString(c.getColumnIndexOrThrow("versionName"));
return bv;
} else {
return null;
}
} finally {
c.close();
}
Very rarely, I get a crash like this, inside the call to c.moveToFirst():
Caused by: android.database.sqlite.SQLiteReadOnlyDatabaseException: attempt to write a readonly database (code 776)
at android.database.sqlite.SQLiteConnection.nativeExecuteForCursorWindow(Native Method)
at android.database.sqlite.SQLiteConnection.executeForCursorWindow(SQLiteConnection.java:845)
at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:836)
at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62)
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:144)
at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:197)
at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:237)
As a workaround, I could try using a writable database connection instead, but I'd like to know why the crash is happening.
The table I'm reading from is a standard SQLite table:
CREATE TABLE BudgetVersions (
entityId VARCHAR PRIMARY KEY NOT NULL UNIQUE,
budgetId VARCHAR NOT NULL,
versionName VARCHAR NOT NULL,
dateFormat VARCHAR,
currencyFormat VARCHAR,
lastAccessedOn DATETIME,
isTombstone BOOL NOT NULL,
deviceKnowledge NUMERIC NOT NULL
);
I've seen the crash happen on both a KitKat emulator and a device running Lollipop.
There is a separate writeable connection open to the same database at the same time, owned by a WebView. The database is being updated by Javascript code in the WebView, and read from in the native Android/Java layer with this read-only connection.
I expect this may prove to be the ultimate cause of the problem, but I'd like to understand in detail why a read-only connection would interfere with a separate writeable connection.
I am well aware that the general advice is to use a single connection to the database, but since the writeable connection is owned by the WebView, I don't have easy access to it from the Java code.
Solved by changing it to a writeable database connection. The clue was in the documentation for the 776 error code:
(776) SQLITE_READONLY_ROLLBACK
The SQLITE_READONLY_ROLLBACK error code is an extended error code for
SQLITE_READONLY. The SQLITE_READONLY_ROLLBACK error code indicates
that a database cannot be opened because it has a hot journal that
needs to be rolled back but cannot because the database is readonly.
During development, I am frequently interrupting the currently-running app to install and run a new version. This causes the currently-running app to be force-stopped by the system. If the Javascript code in the WebView is in the middle of writing to the database via its separate writeable connection when the app is nuked, then a hot journal will be left behind.
When the new version of the app starts up, the read-only database connection in the native Java code is opened. When this connection spots the journal, it tries to roll back the journal. And because it's a read-only connection, it fails.
(This fits with the crash being observed immediately on startup after I've made a change.)
The correct fix is therefore to make the Java connection a writeable connection. This connection never attempts a write during normal operation, but it must write when recovering from a previous interrupted write through the WebView's writeable connection.

sqlite update not releasing memory on HTC desire

Running my app on an HTC Desire (android version 2.3.3) I noticed memory usage increasing when calling the following function:
private static final String TABLE_MODULES = "tbl_module1";
private static final String MOD1_INCLUDE = "mod1_include";
private void setItemInclude (int idx, boolean include)
{
String incString = "";
if (include)
incString = "true";
else
incString = "false";
ContentValues args = new ContentValues();
args.put(MOD1_INCLUDE, incString);
database.update(TABLE_MODULES,args,"_id="+idx,null);
}
where database is of type SQLiteDatabase and was obtained from a call to dbHelper.getWritableDatabase() and where dbHelper is derived from SQLiteOpenHelper and instantiated with the application context.
I get the desired update, but when I check the data usage of my application it appears to increase by about 60KB per call and is not released unless dbHelper.close() is called. Multiple calls to this function result in SQLiteDiskIOException.
I have tried database.rawQuery(), which also updates correctly but hangs on to memory similarly.
String updateQuery = "UPDATE " + TABLE_MODULES + " SET MOD1_INCLUDE = " + "'" + incString + "'" + " WHERE _id ='" + idx + "'";
Cursor cursor = database.rawQuery(updateQuery, null);
cursor.moveToFirst();
cursor.close();
Running the same code on an Asus (android 4.0.3) and an Orange San Francisco (android 2.1.update1) does not have this problem.
getWritableDatabase is called from the activity's onCreate() and the resulting database is closed in onDestroy(). I thought that it was ok to keep the database open for the duration of the activity. Any ideas as to what is the cause?
I have found what is causing the problem.
On the HTC, each time database.update() is called a temporary file is updated in the application's databases directory. This file has the extension .sqlite-wal. This file gets bigger (by 60KB) on each call and is only deleted when I close the database.
The ASUS also creates a temporary file but with the extension .sqlite-journal. This file is also updated on each update() call, but once the transaction completes the file's size reduces to zero. So, no memory problems result.
I am not sure what the difference is between Journal and Wal but believe that they are both to do with rolling back a database after a failed transaction. I have solved the memory problem with the following code, which turns off the journal/wal feature.
Cursor cursor = database.rawQuery("PRAGMA journal_mode=OFF", null);
cursor.moveToFirst();
cursor.close();
Calling this after opening the database appears to prevent these temporary files from being created.
However, I am not confident that this is a good idea. I was surprised not to find others reporting this issue and wonder if leaving the database open ready for multiple transactions is considered bad practice? I am new to SQL and have fairly limited experience with databases.
The documentation is quite clear.
The WAL journaling mode uses a write-ahead log instead of a rollback
journal to implement transactions.
In your case it sounds like automatic checkpointing is either not configured or not working. You probably don't want to turn journaling off as it can result in data loss.

What is the proper technique for updating database tables

I have database updates like the two below, throughout my code. Most of my updates open the database first before updating the record and then closes the database after the record has been updated. I have noticed that not using this statement: mDb = Helper.getWritableDatabase(); before the insertion and this statement: mDb.close(); after insertion, will cause a force close error sometimes, but not always. What is the proper way. Do I use the open and close statements all the time or only when I have to or should I always open and then close during the update process. What is the proper technique. Here is the snippet with the open close statements. Thanks in advance. Is the open statement necessary?
// Open connections to the database
mDb = Helper.getWritableDatabase();
// update 1
String strFilter7 = "_id=" + 7;
ContentValues args7 = new ContentValues();
args7.put(COL_VALUE, newB1ftgvalue);
mDb.update("VarData", args7, strFilter7, null);
// update 2
String strFilter11 = "_id=" + 11;
ContentValues args11 = new ContentValues();
args11.put(COL_VALUE, newB2ftgvalue);
mDb.update("VarData", args11, strFilter11, null);
// closes database
mDb.close();
It is good practice to always call close() after you are done with database updates. If you haven't closed the database you may see errors. Once database is open, you may do multiple updates. It shouldn't be an issue. One thing to take care is, it is better not to keep open connection for long time due to lot of reasons. Here is good discussion on this topic.
Do not close it and do only have one sqlite helper. It's basically a static openhelper. There is no problem with never closing your database. This link gives a good piece of code that works great. You will not have memory leaks with an open database. You will however have problems with open cursors, so make sure to close those.
http://www.touchlab.co/blog/single-sqlite-connection/
A good discussion is here:
What are the best practices for SQLite on Android?
I realized the link I posted has changed since when I once first viewed it. Change:
instance = new DatabaseHelper(context);
to
instance = new DatabaseHelper(context.getApplicationContext());
and
public class DatabaseHelper extends OrmLiteSqliteOpenHelper
to
public class DatabaseHelper extends SqliteOpenHelper
You need to open the db as a writable database in order to modify its data, so yes, you have to open it before updating records.

Preventing db lock exceptions when opening

I have an app that uses a database with 3 tables in it. Those 3 tables have data read from and written to them by activities and services.
Having gotten a few "android.database.sqlite.SQLiteException: database is locked" crashes, I went in to the database adapter class and wrapped every write, update, or delete function with a synchronized statement, so like:
public int deleteExpiredAlarms() {
String whereClause = FIELD_EXPIRED + " = 1";
int val = 0;
synchronized(dbWriteLock) {
val = db.delete(ALARM_DATABASE_TABLE, whereClause, null);
}
return val;
}
That seemed to make it better. But lately it's gotten bad again as I've added more services that read and write to different tables.
Do I need to synchronize ALL db access statements, including queries?
The exception is occurring on the attempt to open the writable database via the open helper...should I synchronize that act also?
I've heard that I should only be using one db helper so that there won't be issues with multiple threads accessing the db. How do I use only one db helper? Every example I've seen so far has the db helper as an instantiated value inside the db adapter....so wouldn't that be a separate db helper per db adapter instantiated (one in an activity, one in a service running,etc)
I've looked at using a content provider instead, as it's been claimed to solve problems like this, but it's really more work than I want to do if I should be able to have direct db access without locking issues. And I do not plan to make this db accessible to other apps.
Thanks for the help.

SQLiteDiskIOException in Android

We are getting a large number of SQLiteDiskIOException errors in our Android app, with stack traces similar to the following:
E/AndroidRuntime( 2252): Caused by: android.database.sqlite.SQLiteDiskIOException: disk I/O error
E/AndroidRuntime( 2252): at android.database.sqlite.SQLiteQuery.native_fill_window(Native Method)
E/AndroidRuntime( 2252): at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:75)
E/AndroidRuntime( 2252): at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:288)
E/AndroidRuntime( 2252): at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:269)
E/AndroidRuntime( 2252): at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:171)
E/AndroidRuntime( 2252): at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:248)
E/AndroidRuntime( 2252): at com.company.android.CActivity$QueryTask.doInBackground(CActivity.java:1660)
This recently starting happening a few weeks ago, but no significant database changes took place in the exact release in which reporting of this issue started. Despite having (I believe) appropriate indices, this exception has been reported more than we are comfortable with on cursor.moveToFirst() calls after the query completes, as you can see from the stack trace. So far, we have been completely unable to reproduce.
The query is somewhat complicated, with three left joins. Below is a representative query, with some identifying columns changed to protect the innocent.
select distinct c._id as _id,c.type as type,c.name as name,c.slug as slug,c.description as description,c.extra1 as extra1,c.extra2 as extra2,c.extra3 as extra3,c.extra4 as extra4,c.extra5,c.extra6 as extra6,c.extra7 as extra7,c.extra8 as extra8, c.extra9 as extra9,c.sslug as sslug,
c2.name as sname,
p.type as prel,
em.dS as dS,em.eS as eS
from cse as c
left join cse as c2 on c.sslug=c2.slug
left join emed as em on c.slug=em.slug
left join pre as p on c.sslug=p.slug
where c.pslug='slug' AND c.user='user' AND c.csource='csource'
order by c.type asc, c.extra6 desc, c.sortorder asc
Other sources have suggested that we are trying to pull out too much data, but this is simply not the case. The three user cases where we have been able to get full database rowcounts show:
cse with < 2000 entries,
emed with <150 entries, and
pre with either 0 or 7 entries.
Furthermore 'explain query plan' on the query indicates all joins are done against indexed columns.
In every case we have seen this, the user was running Android 2.1 on a variety of devices (DROID, Hero, EVO, possibly others). Notably, we have not seen this on a couple G1 devices we have, even when they are loaded down with other apps.
Finally, uninstalling and reinstalling has proven successful in cleaning up the issue, though possibly only temporarily.
I fear this problem is a result of data corruption in Android 2.1.
Does anyone have possible suggestions of what to look into?
Could this be related to this Android bug on SQLiteDatabaseCorruptException
Guidance and solutions are much appreciated.
It seems that you have multi threading issue, one thread tries to get the data while another one or more than one are trying to insert some data into your tables,because the exception is thrown from method (getCount).
Also don't forget; SQLite cursor is not internally synchronized, so if you use this cursor from multiple threads you should perform your own sync mechanism.
We have similar problem. When our application starts souch error occurs.
In onCreate method we check is there any database in application path. If any database occurs, we invoke such code
public boolean createDataBase() throws IOException
{
boolean dbExist = checkDataBase();
if(dbExist)
{
//do nothing - database already exist
}
else
{
//By calling this method and empty database will be created into the default system path
//of your application so we are gonna be able to overwrite that database with our database.
this.getReadableDatabase();
}
return dbExist;
}
private boolean checkDataBase()
{
SQLiteDatabase checkDB = null;
try
{
String myPath = DATABASE_PATH + DATABASE_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}
catch(SQLiteException e)
{
}
if(checkDB != null)
{
checkDB.close();
}
return checkDB != null ? true : false;
}
And if there is no database(first run of application) we copy it from resources.
But some times occurs SQLiteDiskIOException and database calls from resources.
one reason is as Bassel Kh stated, multi thread problom, the another is the db is unavailable, ie. db is deleted or sdcard is unavaliable.

Categories

Resources