在我们公司,我们正在开发一个显示时间表的应用程序。我们愿意让用户(几乎)无限期地滚动它。
现在有两个事实需要考虑:
在当前的实现中,我们默认加载了40个项目,然后当用户滚动超过某个阈值时,我们通过将限制增加到40 + 20项来重复查询,依此类推。
然而,这种方法似乎相当弱因为它与之前陈述的原则发生冲突:查询最终会变得相当大,并且在某些时候光标可能会达到1MB的内存限制(我们加载了很多字符串。)
现在我们正在考虑利用MergeCursor并继续这样做:
您如何看待这种方法?任何弱点(开销除外,应该很小)?
如果您能指出/描述更好的解决方案吗?
提前致谢
答案 0 :(得分:0)
在评论中,pskink建议使用AbstractWindowedCursor
。
我对这门课程并不熟悉并对其进行了一些调查。事实证明SQLiteCursor
已经扩展了它。文档说明了这一点:
光标拥有它使用的光标窗口。当光标关闭时,其窗口也会关闭。同样,当光标使用的窗口改变时,其旧窗口关闭。这种严格所有权的策略可确保光标窗口不会泄露。
这意味着在任何给定时刻,只有一小部分从DB查询的数据实际上保存在内存中。这是SQLiteCursor
中代码的有趣部分:
@Override
public boolean onMove(int oldPosition, int newPosition) {
// Make sure the row at newPosition is present in the window
if (mWindow == null || newPosition < mWindow.getStartPosition() ||
newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
fillWindow(newPosition);
}
return true;
}
@Override
public int getCount() {
if (mCount == NO_COUNT) {
fillWindow(0);
}
return mCount;
}
private void fillWindow(int requiredPos) {
clearOrCreateWindow(getDatabase().getPath());
try {
if (mCount == NO_COUNT) {
int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0);
mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true);
mCursorWindowCapacity = mWindow.getNumRows();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "received count(*) from native_fill_window: " + mCount);
}
} else {
int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos,
mCursorWindowCapacity);
mQuery.fillWindow(mWindow, startPos, requiredPos, false);
}
} catch (RuntimeException ex) {
// Close the cursor window if the query failed and therefore will
// not produce any results. This helps to avoid accidentally leaking
// the cursor window if the client does not correctly handle exceptions
// and fails to close the cursor.
closeWindow();
throw ex;
}
}
这意味着两件事:
CursorWindow
)在内存中。 1MB大小限制是(可能)一个神话或它引用CursorWindow对象,在这种情况下它是一个安全的大小mCount
变量中的数据集的总大小)可能会对感知性能产生一些影响。我需要进一步测试这个。总之,很可能没有必要使用MergeCursor技巧或过分担心OOM。
我可以在源代码中进行更好的调查,但我对网上看到的内容感到有点不知所措。