IllegalStateExceptio: Couldn't read row * from CursorWindow due to CursorWindow.nativeGetLong

来源:互联网 发布:北京万户网络怎么样 编辑:程序博客网 时间:2024/06/08 10:18

本人在开发云端备份短信查询短信过程中遇到了如下报错问题,被这个报错折腾了将近一天时间,现将解决思路Mark,错误之处请勘误。
05-13 19:24:23.635847 25932 28515 E AndroidRuntime: FATAL EXCEPTION: SyncAdapterThread-1
05-13 19:24:23.635847 25932 28515 E AndroidRuntime: Process: com.xx, PID: 25932
05-13 19:24:23.635847 25932 28515 E AndroidRuntime: java.lang.IllegalStateException: Couldn’t read row 2166, col 0 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it.
05-13 19:24:23.635847 25932 28515 E AndroidRuntime: at android.database.CursorWindow.nativeGetLong(Native Method)
05-13 19:24:23.635847 25932 28515 E AndroidRuntime: at android.database.CursorWindow.getLong(CursorWindow.java:511)
05-13 19:24:23.635847 25932 28515 E AndroidRuntime: at android.database.AbstractWindowedCursor.getLong(AbstractWindowedCursor.java:75)
05-13 19:24:23.635847 25932 28515 E AndroidRuntime: at android.database.CursorWrapper.getLong(CursorWrapper.java:127)
05-13 19:24:23.635847 25932 28515 E AndroidRuntime: at com.xx.sync.resource.sms.LocalMessage.getWithoutFileName(LocalMessage.java:68)
……

  • 从报错日志看是查第2166个短信记录第一列数据时报错,网上走捷径查类似问题分析,有牛牛指出先看所查表是否有该列属性以及该列属性类型是否匹配,经确认分析排除该种可能;

  • 无计可施,只能查看报错源码,看是什么原因导致该类异常,相关代码如下:

static jlong android_database_CursorWindow::nativeGetLong(JNIEnv* env, jclass clazz, jlong windowPtr,        jint row, jint column) {    CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);    LOG_WINDOW("Getting long for %d,%d from %p", row, column, window);    CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);    if (!fieldSlot) {        throwExceptionWithRowCol(env, row, column);        return 0;    }    ...}

window->getFieldSlot()函数如下:

CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {    if (row >= mHeader->numRows || column >= mHeader->numColumns) {        ALOGE("Failed to read row %d, column %d from a CursorWindow which "                "has %d rows, %d columns.",                row, column, mHeader->numRows, mHeader->numColumns);        return NULL;    }    RowSlot* rowSlot = getRowSlot(row);    if (!rowSlot) {        ALOGE("Failed to find rowSlot for row %d.", row);        return NULL;    }    FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(rowSlot->offset));    return &fieldDir[column];}

根据上面的代码,在报错日志中也找到了类似日志:
05-13 19:24:23.088027 2389 2406 W CursorWindow: Window is full: requested allocation 204 bytes, free space 116 bytes, window size 2097152 bytes

05-13 19:24:23.634110 25932 28515 E CursorWindow: Failed to read row 2166, column 0 from a CursorWindow which has 2165 rows, 17 columns.

  • 从上面日志看是查到了2166行符合条件的短信记录,但实际只有2165个记录导致查询第2166个记录报错。接着问题就来了,为什么查到了2166行记录而实际只有2165行记录呢?
  • 了解CursorWindow query机制有关的都知道通过数据库或provider query到大量符合条件的数据时,由于CursorWindow有内存大小限制,query接口只能返回一部分记录,我们通过Cursor.moveToNext获取记录,在读取符合条件记录过程中应该还会再去query装载后面没装上的记录,如果这个过程中有其他线程update了该query条件的记录导致后面query返回的记录数比第一次查询到的更少就会导致上面的问题,因此在查询到大量符合条件记录并在Cursor.moveToNext返回false前是不能有update或delete等导致query符合条件的记录变少的操作。下面我们需要再分析日志验证是否有其他线程在update操作。

05-13 19:24:23.213615 25932 28004 D YCloud.Message: rawMessageURI -> content://sms/8447?account_name=zz&account_type=xx&caller_is_syncadapter=true
05-13 19:24:23.213650 25932 28004 D ZsCloud.Message: URI -> content://sms/8447
05-13 19:24:23.263966 2389 24797 D TelephonyProvider: SmsProvider->update content://sms/8447 succeeded
从上面日志可以看出25932进程的28004线程更新了一条短信,而报错日志显示是25932进程的28515线程在查询短信,上面假设分析得到验证。
05-13 19:24:23.635847 25932 28515 E AndroidRuntime: at android.database.CursorWindow.getLong(CursorWindow.java:511)

通过上面的分析总结是由于有个线程再查询大量符合条件记录时有其他线程更新这些符合查询条件的记录导致,这就需要结合业务进行同步控制,避免两者同时进行。

阅读全文
0 0
原创粉丝点击