【问题标题】:Updating Cursor in background thread在后台线程中更新光标
【发布时间】:2011-11-14 21:29:27
【问题描述】:

我有一个与in this discussion 描述的问题类似的问题:当底层数据库发生更改时,我需要刷新 ListView,但查询成本很高,所以我在 AsyncTask 中进行。

当更新的光标准备好时,我会这样做。 (这也是列表最初在启动时填充的方式。)

@Override
protected void onPostExecute(Cursor result) {
    if (activity != null) {
        if (currentCursor != null) {
            // existing cursor is closed by adapter.changeCursor() so
            // we don't need to explicitly close it here 
            stopManagingCursor(currentCursor);
        }
        currentCursor = result;
        startManagingCursor(currentCursor);

        if (adapter == null) {
            adapter = getAdapter(result);
            setListAdapter(adapter);
        } else {
            adapter.changeCursor(result);
        }

        activity.onGotList(result, dbAdapter);
    }
}

这是我得到的错误。并非每次都发生,这更令人沮丧。

Releasing statement in a finalizer. Please ensure that you explicitly call close() on your cursor: SELECT DISTINCT   t._id AS _id,   t.amount,   t.date,   t.memo,   t.synced,   t.flag,   (children.pa
android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
     at android.database.sqlite.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:62)
     at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:100)
     at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:46)
     at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:53)
     at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1412)
     at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1382)

所以,我显然没有正确关闭光标。如果我调用currentCursor.close() 而不是依靠adapter.changeCursor() 关闭传出游标,那么我会收到关于关闭游标两次或关闭null 游标的警告。

这样做的正确方法是什么?

在我链接到的讨论中,Dianne Hackborn 建议改用Loader。这不是我的选择,因为我的代码必须在 Android 2.1 上运行。

【问题讨论】:

  • using a Loader instead. That is not an option for me since my code has to run on Android 2.1. 是的,这是一个选项,因为有一个兼容库 v4
  • @Selvin 哦,谢谢,我忘记了。我会调查的。

标签: android cursor background-thread


【解决方案1】:

在 Activity 暂停或终止时尝试 .close() 游标。 在活动的 onPause() 或 onDestroy() 部分中。

【讨论】:

  • 请解释您认为这将如何帮助。据我所知,光标的生命周期由活动管理,因为我在上面调用startManagingCursor()
  • 在我的应用中像我描述的那样工作。在活动的 OnPause() 部分中,我输入: super.onPause(); mProductCursor.deactivate(); mProductCursor.close();
【解决方案2】:

基本上,从两个不同的助手访问同一个数据库是可能的,但非常糟糕的做法,所以如果你有一个执行数据库查询的活动,你不应该也有一个线程访问它,否则 android 会抛出一个安静的logcat报错,然后就忘记查询了……

我发现的最佳解决方案是实现一个可运行线程池,每个线程池都是一个数据库查询,它们都使用相同的数据库助手。因此,任何时候只有一个线程在访问数据库,并且在线程池启动/停止时数据库只是打开和关闭。

线程池模式的实现可以在这里找到:http://mindtherobot.com/blog/159/android-guts-intro-to-loopers-and-handlers/

【讨论】:

  • 我只有一个数据库助手。
  • 嗯,一个助手类,或者一个助手实例在任何时候运行。另外,确保在你完成后总是调用助手的 close() (通常在 onDestroy() 中)。
  • 一个在我的应用程序生命周期内保持打开状态的助手实例。
  • 你确定在完成后使用 close() 吗?此外,在代码的早期版本中,您可能没有关闭数据库,因此错误持续到此版本。你试试清除你应用的数据怎么样?
【解决方案3】:

如果您没有更改任何其他内容,那么从列表中重新绘制是有必要更改光标的。你能摆脱只需要当前的适配器吗?

类似

的东西
adapter.getCursor().requery();

虽然如果你在主 ui 线程以外的线程中,你可能想用它来调用它

//Did not realize this was deprecated Thanks to Graham Borland for the heads up
runOnUiThread(new Runnable(){
    public void run(){
        adapter.getCursor().requery();
    }
});

取决于您的设置。

新的解决方案仍需要测试,并确保它不会引起问题,显然 startManaginCursor 和 stopManaginCursor 也已被弃用,因此该解决方案也不值得。

stopManagingCursor(adapter.getCursor());
if (!adapter.getCursor().isClosed()) 
    adapter.getCursor().close();
//cursor creation stuff here if needed
startManagingCursor(newCursor);
adapter.changeCursor(newCursor);

【讨论】:

  • requery() 已弃用。我用新光标替换现有光标的方法显然是推荐的:developer.android.com/reference/android/database/…
  • 好吧,对不起,我想我错过了,我想我很快就会和你在同一条船上玩一些代码。如果我找到更好的方法会回复。
  • 这是我在我的一个测试应用程序上必须做的事情,不确定它是否好但需要运行更多测试并进行一些错误/空检查,但它可以在没有光标关闭错误的情况下工作。 stopManagingCursor(adapter.getCursor()); if (!adapter.getCursor().isClosed()) adapter.getCursor().close();开始管理光标(新光标);相册.changeCursor(newCursor);
  • 最后一张专辑应该是我更换失败的适配器。事物。它的要点是我直接通过适配器管理光标,而不是在光标上保持标签。
猜你喜欢
  • 2017-11-15
  • 1970-01-01
  • 2012-04-24
  • 2013-11-23
  • 1970-01-01
  • 2015-03-20
  • 2014-04-18
  • 2011-12-25
  • 1970-01-01
相关资源
最近更新 更多