Wishmaster Wishmaster - 7 months ago 34
SQL Question

CursorWindowAllocationException in standard ORMLite method

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?

Answer

If you look up the source of CursorWindowAllocationException it reads:

This exception is thrown when a CursorWindow couldn't be allocated, most probably due to memory not being available.

If you follow the stack, you'll see that the call com.j256.ormlite.android.AndroidDatabaseConnection.queryForLong is creating a cursor for every createOrUpdate 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.

actionDao.callBatchTasks(new Callable<Void>() {
        public Void call() throws SQLException {
            for (Action action : actionList) {
                actionDao.createOrUpdate(action);
            }
        return null;
    }
});