"Failure 21 (out of memory)" when performing some SQLite operations
My app synchronizes its local SQLite database with an external database that exists on Amazon's servers. Occasionally, the synchronization fails and produces this logcat output:
08-15 19:58:38.149: ERROR/Database(343): Failure 21 (out of memory) on 0x0 when preparing 'drop table if exists keymap2'.
08-15 19:58:38.149: ERROR/KeymapDbHelper(343): Error occurred while syncing DB
08-15 19:58:38.149: ERROR/KeymapDbHelper(343): android.database.sqlite.SQLiteException: unknown error: drop table if exists keymap2
08-15 19:58:38.149: ERROR/KeymapDbHelper(343): at android.database.sqlite.SQLiteDatabase.native_execSQL(Native Method)
08-15 19:58:38.149: ERROR/KeymapDbHelper(343): at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1610)
08-15 19:58:38.149: ERROR/KeymapDbHelper(343): at lee.medical.icu.dataentry.db.KeymapDbHelper.syncToTempTable(KeymapDbHelper.java:114)
08-15 19:58:38.149: ERROR/KeymapDbHelper(343): at lee.medical.icu.dataentry.db.KeymapDbHelper.syncDb(KeymapDbHelper.java:84)
08-15 19:58:38.149: ERROR/KeymapDbHelper(343): at lee.medical.icu.dataentry.MainMenuActivity$DbSyncTask.doInBackground(MainMenuActivity.java:221)
08-15 19:58:38.149: ERROR/KeymapDbHelper(343): at lee.medical.icu.dataentry.MainMenuActivity$DbSyncTask.doInBackground(MainMenuActivity.java:1)
08-15 19:58:38.149: ERROR/KeymapDbHelper(343): at android.os.AsyncTask$2.call(AsyncTask.java:185)
08-15 19:58:38.149: ERROR/KeymapDbHelper(343): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
08-15 19:58:38.149: ERROR/KeymapDbHelper(343): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
08-15 19:58:38.149: ERROR/KeymapDbHelper(343): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
08-15 19:58:38.149: ERROR/KeymapDbHelper(343): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
08-15 19:58:38.149: ERROR/KeymapDbHelper(343): at java.lang.Thread.run(Thread.java:1096)
When this happens, my app notifies me that synchronization failed, so I ask it to attempt synchronization again, which works about 50-75% of the time.
Here is the code that performs the synchronization:
@SuppressWarnings("unchecked")
private void syncToTempTable(SQLiteDatabase db, String table)
throws DatabaseMismatchException {
String awsSql = "select * from " + keymapDb;
SelectRequest select = new SelectRequest(awsSql);
SelectResult result;
Log.d(TAG, "Making request: " + awsSql);
result = simpleDB.select(select); // Could throw AmazonClientException
List<Item> items = result.getItems();
db.execSQL("drop table if exists " + table);
String tempSql = "create table " + table + " (" +
C_KEY + " text primary key, " +
C_NAME + " text)";
db.execSQL(tempSql);
Log.d(TAG, "SQL executed: " + tempSql);
ContentValues cv = new ContentValues();
for (Item i : items) {
cv.clear();
String key = i.getName();
cv.put(C_KEY, key);
List<Attribute> attributes = i.getAttributes();
assert attributes.size() <= 1;
for (Attribute a : attributes) {
String name = a.getName();
String value = a.getValue();
if ((key.equals(R_INFO) && !name.equals(C_REVISION)) ||
(!key.equals(R_INFO) && !name.equals(C_NAME)))
throw new DatabaseMismatchException("DB not synced because " +
"column " + name + " was unexpected");
cv.put(C_NAME, value);
}
db.开发者_如何学编程insert(table, null, cv);
}
}
Line 114 is db.execSQL("drop table if exists " + table);
The purpose of the line is to drop the temporary table if, for some reason, the app had crashed beforehand and prevented the table from being dropped as it normally would do so. I've noticed that if that table is left alone, the app will crash when it tries to create a new table with the same name.
Why does my app run out of memory here, and what can I do to fix it?
Edit: Okay, it actually happens every other time the app syncs with the external database.
Turns out that this error was the result of a programming error before my app made this method invocation. Before I made the call to syncToTempTable()
, I opened the database:
SQLiteDatabase db = dbHelper.getWritableDatabase();
Right after this, I wanted to check the revision number of the local database. The method that I used to do that also opened the database (so basically I opened my database twice) and then closed it (which likely closed all open streams to the database). Because of this, I didn't realize that I was giving syncToTempTable()
a closed database.
Problem solved now.
精彩评论