【问题标题】:Managing database connections in an Android Activity在 Android Activity 中管理数据库连接
【发布时间】:2009-12-28 17:47:55
【问题描述】:

我有一个带有 ListActivity 的应用程序,它使用 CursorAdapter 作为其适配器。 ListActivity 打开数据库并查询 CursorAdapter,这一切都很好,但我在确定何时关闭 Cursor 和 SQLiteDatabase 时遇到问题。

现在处理事情的方式,如果用户完成活动,我关闭数据库和光标。但是,这仍然会导致 DalvikVM 警告我我已经打开了一个数据库 - 例如,如果用户点击“主页”按钮(将活动留在任务堆栈中),而不是“返回”按钮。

如果我在暂停期间关闭它们,然后在恢复期间重新查询,那么我不会收到任何错误,但是如果没有重新查询,用户将无法返回到列表(从而失去用户在列表中的位置)。我的意思是,用户可以单击列表中的任何项目并基于它打开一个新的活动,但通常会希望在之后点击“返回”并返回到列表中的同一位置。如果我重新查询,那么我无法将用户返回到正确的位置。

处理此问题的正确方法是什么?我希望列表保持正确滚动,但我不希望 VM 一直抱怨未关闭的数据库。

编辑:以下是我目前如何处理代码的大致轮廓:

public class MyListActivity extends ListActivity {
    private Cursor mCursor;
    private CursorAdapter mAdapter;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mAdapter = new MyCursorAdapter(this);
        setListAdapter(mAdapter);
    }

    protected void onPause() {
        super.onPause();
        if (isFinishing()) {
            mCursor.close();
        }
    }

    protected void onDestroy() {
        super.onDestroy();
        mCursor.close();
    }

    private void updateQuery() {
        // If we had a cursor open before, close it.
        if (mCursor != null) {
            mCursor.close();
        }
        MyDbHelper dbHelper = new MyDbHelper(this);
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        mCursor = db.query(...);
        mAdapter.changeCursor(mCursor);
        db.close();
    }
}

updateQuery() 可以被多次调用,因为用户可以通过菜单项过滤结果(我在代码中省略了这部分,因为即使用户不进行过滤,问题仍然存在)。

同样,问题是当我回家时出现泄漏错误。然而,回家后,我可以回到应用程序并再次找到我的列表 - 光标完好无损。

【问题讨论】:

  • "现在的处理方式,如果用户完成活动,我关闭数据库和光标。"你可以再详细一点吗?您在哪个 Android 事件上实现此代码(例如 onDestroy())?
  • 目前在 onPause() 中。如果 isFinishing() 为真,我只调用 close()。

标签: android sqlite


【解决方案1】:

我通常每个应用都有一个SQLiteOpenHelper

public class MyApp extends Application {

 private static MyDbHelper dbHelper;

 @Override
 public void onCreate() {
  super.onCreate();
  dbHelper = new MyDbHelper(this);
 }

 @Override
 public void onTerminate() {
  super.onTerminate();
  dbHelper.close();
 }

 public static SQLiteDatabase getDB() {
  return dbHelper.getWritableDatabase();
 }

}

到目前为止,这种方法没有任何问题。
你可以使用Activity子类中的startManagingCursor(..)(虽然我让Adapters管理Cursors):

public class SomeAdapter extends CursorAdapter {

    public void setNewFilter(CharSequense query){
        // in fact, I use a DAO here
        Cursor c = MyApp.getDB().query(..);
        changeCursor(c);
    }

}

【讨论】:

  • 您不应该依赖在真实设备上调用 onTerminate()。请参阅该方法的文档为什么。
【解决方案2】:

我不同意。查看 android 文档,特别是这张图片 http://developer.android.com/images/activity_lifecycle.png 我会说资源应该在 onstart() 方法中创建并在 onstop() 方法中清理。这就是谷歌在他们的代码中处理它的方式,google analytics docs for android

【讨论】:

    【解决方案3】:

    我建议使用抽象工厂方法来确保您的 SQLiteDatabase 仍然是单例。查看我关于该主题的博文:

    Correctly Managing Your SQLite Database

    【讨论】:

      【解决方案4】:

      关闭onDestroy() 中的CursorSQLiteDatabase,对于那些尚未关闭的东西,看看是否有帮助。在某些情况下,isFinishing()onPause() 中是false,但最终仍会调用onDestroy()

      【讨论】:

      • 这并不能解决我遇到的主要问题,即当用户点击“主页”按钮时,数据库没有关闭。当用户点击“home”时不会调用 onDestroy(),但如果用户确实点击了“home”并转到另一个应用程序,那么 Activity 将被终止,而不会执行另一行代码。我不能在 onPause() 或 onStop() 期间调用 close(),因为那还为时过早。也许这只是Android的一个限制,但我不喜欢看到那些未封闭的数据库。也许数据库池可以解决这个问题?
      • 如果您的 SQLiteDatabase 对象被垃圾回收且未关闭,您将收到“发现泄漏”异常。因此,当您的活动停止时,您必须做一些事情来释放该 SQLiteDatabase 对象。
      • 我添加了一些代码来显示我在做什么。我真的不确定如何才能更好地阻止泄漏,也许这会有所帮助。
      • 我很惊讶你的代码能正常工作。我认为您必须在游标打开时保持数据库打开。您何时收到泄漏消息:立即按下 HOME,在按下 HOME 后坐在主屏幕上一段时间后,从启动器重新启动应用程序,或其他什么?
      • 最初,我在光标打开时保持数据库打开;但后来我发现即使我关闭了数据库,代码也能正常工作。无论如何,在按下 HOME 并在上面坐一会儿后,你会得到错误。
      【解决方案5】:

      通常是的

      onDestroy()
      

      是您应该关闭数据库并在

      中打开它的位置
      onCreate()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-05-11
        • 1970-01-01
        • 2012-04-01
        • 2021-02-18
        • 2014-11-13
        • 1970-01-01
        • 1970-01-01
        • 2012-08-17
        相关资源
        最近更新 更多