CursorWindowAllocationException in standard ORMLit

2019-06-18 15:21发布

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?

2条回答
该账号已被封号
2楼-- · 2019-06-18 16:06

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.

查看更多
beautiful°
3楼-- · 2019-06-18 16:17

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;
    }
});
查看更多
登录 后发表回答