錯誤信息
08-30 20:27:36.751 E/CursorWindow( 760): Could not allocate CursorWindow '/data/data/com.android.providers.media/databases/external.db' of size 2097152 due to error -12.
08-30 20:27:36.771 E/JavaBinder( 760): *** Uncaught remote exception! (Exceptions are not yet supported across processes.)
08-30 20:27:36.771 E/JavaBinder( 760): android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed. # Open Cursors=781 (# cursors opened by pid 3105=781)
08-30 20:27:36.771 E/JavaBinder( 760): at android.database.CursorWindow.<init>(CursorWindow.java:104)
08-30 20:27:36.771 E/JavaBinder( 760): at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198)
08-30 20:27:36.771 E/JavaBinder( 760): at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:162)
08-30 20:27:36.771 E/JavaBinder( 760): at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:156)
08-30 20:27:36.771 E/JavaBinder( 760): at android.database.CursorToBulkCursorAdaptor.count(CursorToBulkCursorAdaptor.java:184)
08-30 20:27:36.771 E/JavaBinder( 760): at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:117)
08-30 20:27:36.771 E/JavaBinder( 760): at android.os.Binder.execTransact(Binder.java:338)
08-30 20:27:36.771 E/JavaBinder( 760): at dalvik.system.NativeStart.run(Native Method)
錯誤原因
CursorWindow緩存數據達到最大限制(2M不同的機器和SQLite版本其值可能不同)后,仍有查詢結果集需要緩存,在申請內存分配時申請失敗發生了OOM內存溢出;SQLite查詢出的數據集cursor,都由native層的CursorWindow進行數據管理,包括內存空間的申請和數據的填充。CursorWindow實際上是共享內存的抽象,以實現跨進程,跨應用數據共享(ContentProvider作為數據通道,也支持跨進程,跨應用的數據訪問)
在ContentProvider端透過SQLiteDatabase的封裝查詢到的數據集保存在CursorWindow所指向的共享內存中,然后通過Binder把這片共享內存傳遞到ContentResolver端,即查詢端。這樣客戶就可以通過Cursor來訪問這塊共享內存中的數據集了。
解決辦法
保證CursorWindow不會達到最大限制):
1.只查詢需要的字段;
根據UI顯示需要,或實際需要查詢的字段進行查詢,盡量不會表查詢
2.二進制文件不要存在數據庫中;
數據庫僅適用于保存一些較短文字,整數,布爾,浮點數等一些,易于查詢和操作的輕量級的數據,目的也是在于快速搜索和查詢。對于像圖片,較長的文字(如文章)等大數據,最好直接以文件形式存儲在硬盤中,然后在數據庫保存它們的訪問路徑
3.對于大數據量的查詢采用分段查詢方式;
無論表中的一條記錄數據量如何的小,當條數達到5000級或者萬級或者更多的時候,還是會達到最大的限制
4.正確的關閉Cursor,釋放CursorWindow中不用的資源(需手動調用釋放native中的資源,類似3.0之前的Bitmap需要手動釋放。調用close的必要性:http://www.lxweimin.com/p/3b433ed25aaf
Cursor c;
try {
c = queryCursor();
int a = c.getInt(1);
......
// 如果出錯,后面的cursor.close()將不會執行
//c.close();
} catch (Exception e) {
} finally{
if (c != null) {
c.close();
}
}
如果你的Cursor需要在Activity的不同的生命周期方法中打開和關閉,那么一般可以這樣做:
在onCreate()中打開,在onDestroy()中關閉;
在onStart() 中打開,在onStop() 中關閉;
在onResume()中打開,在onPause() 中關閉;
即要在成對的生命周期方法中打開/關閉
如果程序中使用了CursorAdapter(例如Music),那么可以使用它的changeCursor(Cursor cursor)方法同時完成關閉舊Cursor使用新Cursor的操作。