I need save some objects in DB. I'm using this code in my Dao class.
public void saveActions(List<Action> actionList) throws SQLException {
for (Action action : actionList) {
createOrUpdate(action);
}
}
And sometimes I have CursorWindowAllocationException in createOrUpdate()
function.
Here is stacktrace of error.
java.lang.RuntimeException: An error occured while executing doInBackground() at android.os.AsyncTask$3.done(AsyncTask.java:300) at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355) at java.util.concurrent.FutureTask.setException(FutureTask.java:222) at java.util.concurrent.FutureTask.run(FutureTask.java:242) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) at java.lang.Thread.run(Thread.java:841) Caused by: android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. at android.database.CursorWindow.(CursorWindow.java:109) at android.database.CursorWindow.(CursorWindow.java:100) at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198) at android.database.sqlite.SQLiteCursor.clearOrCreateWindow(SQLiteCursor.java:301) at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:139) 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) at com.j256.ormlite.android.AndroidDatabaseResults.first(SourceFile:73) at com.j256.ormlite.android.AndroidDatabaseConnection.queryForLong(SourceFile:234) at com.j256.ormlite.stmt.StatementExecutor.ifExists(SourceFile:603) at com.j256.ormlite.dao.BaseDaoImpl.idExists(SourceFile:807) at com.j256.ormlite.dao.BaseDaoImpl.createOrUpdate(SourceFile:335) at com.deliveryclub.command.db.SaveActionsCommand.execute(SourceFile:11) at ru.surfstudio.command.CommandExecutor.executeCommand(SourceFile:34) at ru.surfstudio.background.CommonAsyncTask.doInBackground(SourceFile:10) at android.os.AsyncTask$2.call(AsyncTask.java:288) at java.util.concurrent.FutureTask.run(FutureTask.java:237) ... 4 more android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. at android.database.CursorWindow.(CursorWindow.java:109) at android.database.CursorWindow.(CursorWindow.java:100) at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198) at android.database.sqlite.SQLiteCursor.clearOrCreateWindow(SQLiteCursor.java:301) at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:139) 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) at com.j256.ormlite.android.AndroidDatabaseResults.first(SourceFile:73) at com.j256.ormlite.android.AndroidDatabaseConnection.queryForLong(SourceFile:234) at com.j256.ormlite.stmt.StatementExecutor.ifExists(SourceFile:603) at com.j256.ormlite.dao.BaseDaoImpl.idExists(SourceFile:807) at com.j256.ormlite.dao.BaseDaoImpl.createOrUpdate(SourceFile:335) at com.deliveryclub.command.db.SaveActionsCommand.execute(SourceFile:11) at ru.surfstudio.command.CommandExecutor.executeCommand(SourceFile:34) at ru.surfstudio.background.CommonAsyncTask.doInBackground(SourceFile:10) at android.os.AsyncTask$2.call(AsyncTask.java:288) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) at java.lang.Thread.run(Thread.java:841)
UPD Action class
@SuppressWarnings("serial")
@DatabaseTable(tableName = "Action")
public class Action implements Serializable {
public static final String SERVICE_ID = "SERVICE_ID";
public static final String CITY_ID = "CITY_ID";
public static final String CATEGORY_ID = "CATEGORY_ID";
public static final String TITLE = "TITLE";
public static final String SERVICE_TITLE = "SERVICE_TITLE";
public static final String DESCRIPTION = "DESCRIPTION";
public static final String DEADLINE = "DEADLINE";
public static final String PERCENTAGE = "PERCENTAGE";
public static final String PICTURE_URL = "PICTURE_URL";
@DatabaseField(columnName = SERVICE_ID, id = true, useGetSet = true)
private int serviceId;
@DatabaseField(columnName = CITY_ID, useGetSet = true)
private int cityId;
@DatabaseField(columnName = CATEGORY_ID, useGetSet = true)
private int categoryId;
@DatabaseField(columnName = TITLE, useGetSet = true)
private String title;
@DatabaseField(columnName = SERVICE_TITLE, useGetSet = true)
private String serviceTitle;
@DatabaseField(columnName = DESCRIPTION, useGetSet = true)
private String description;
@DatabaseField(columnName = DEADLINE, useGetSet = true)
private Date deadline;
@DatabaseField(columnName = PERCENTAGE, useGetSet = true)
private int percentage;
@DatabaseField(columnName = PICTURE_URL, useGetSet = true)
private String pictureUrl;
public Action() {
}
public int getServiceId() {
return serviceId;
}
public void setServiceId(int serviceId) {
this.serviceId = serviceId;
}
public int getCityId() {
return cityId;
}
public void setCityId(int cityId) {
this.cityId = cityId;
}
public int getCategoryId() {
return categoryId;
}
public void setCategoryId(int categoryId) {
this.categoryId = categoryId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getServiceTitle() {
return serviceTitle;
}
public void setServiceTitle(String serviceTitle) {
this.serviceTitle = serviceTitle;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getDeadline() {
return deadline;
}
public void setDeadline(Date deadline) {
this.deadline = deadline;
}
public int getPercentage() {
return percentage;
}
public void setPercentage(int percentage) {
this.percentage = percentage;
}
public String getPictureUrl() {
return pictureUrl;
}
public void setPictureUrl(String pictureUrl) {
this.pictureUrl = pictureUrl;
}
Does anyone have solution of this problem?
You must call
cursor.close();
in finally { } block when you do not need cursor anymore. Cursor allocation will fail without calling close() on cursor after some time when system resources for cursor allocation will be not available.If you look up the source of
CursorWindowAllocationException
it reads:If you follow the stack, you'll see that the call
com.j256.ormlite.android.AndroidDatabaseConnection.queryForLong
is creating a cursor for everycreateOrUpdate
call.So what's likely happening here is that there are too many
Cursors
being created before the memory is freed.You should execute these calls in a transaction, or better yet, use batch tasks. E.g.