2014年02月08日

ListViewでCursorAdapterを使っている際にWindow is fullが出た

たまにはAndroidのtips的なネタでも書かないと、Android開発者として忘れ去られそうなので書いてみる。

ListViewでCursorAdapterを使ってリストを表示していたらlogcatに、
W/CursorWindow(1111): Window is full: requested allocation 111 bytes, free space 11 bytes, window size 2097152 bytes
みたいなログが出てて、アプリの挙動がおかしくなることがあった。

原因は、ログのメッセージの通り、windows用のメモリが足りませんってことなんだけど、どうにかしてサイズを拡張したいので方法を調べてみた。
まずは、CursorWindow.javaのソースを眺めてみると、window sizeはsCursorWindowSizeで持っていて、
com.android.internal.R.integer.config_cursorWindowSize
から取得していた。しかし、この値はシステムで固定、しかも、privateだし、finalだし、外部から変更するためのメソッドが用意されていない。
ということで、リフレクションを使ってこの値を書き換えてみた。以下が、そのコードです。
static private void setCursorWindowSize(int size){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
try {
final Class cls = Class.forName("android.database.CursorWindow");
final Field fld = cls.getDeclaredField("sCursorWindowSize");
fld.setAccessible(true);
int before = fld.getInt(null); // default=2048*1024
fld.setInt(null, size * 1024); // extend to
int after = fld.getInt(null);
Log.d(TAG, "sCursorWindowSize:" + before + "/" + after);
} catch (Exception e) {
Log.e(TAG, e);
}
}
}

自分は、ApplicationクラスのonCreate内で、
setCursorWindowSize(8192);
みたいな感じで呼び出しました。
これで、上記のログは出なくなりました。試しに、試しにものすごい小さい値に書き換えてみたら、ログ出まくりだったので、一応指定したサイズで機能しているようです。

ちなみに、window sizeが有効なのはICS以降のようです。
(Honeycombのソースは確認できなかったので、もしかしたらHoneycomb以降かもしれませんが。Ginger Breadのソースには無かったです)

今回は、アプリ側での対応方法でしたが、もし、システム全体で拡張するならXposed module作ってしまうと手っ取り早いかもしれない。

同じような現象にお悩みの方がいたら、解決の参考になれば幸いです。
posted by まつも at 22:05| Comment(0) | TrackBack(0) | Android | このブログの読者になる | 更新情報をチェックする