安卓数据库连接和游标问题

3

我已经阅读了很多关于如何在Android开发中创建和使用数据库连接的博客和教程。虽然我有很多可用的工作示例,但不同的实现方式会导致不同的问题。

例如,我使用一个数据源类Datasource和一个数据库帮助类DBManagement

数据源

public class DataSource {
    // Database fields
    private SQLiteDatabase database;
    private DBManagement dbHelper;

    public SMSDataSource(Context context) {
        dbHelper = new DBManagement(context);
    }

    public void open() throws SQLException {
        if(database == null){
             database = dbHelper.getWritableDatabase();
        }
    }

public Cursor exampleCursor(long constraint){
    Cursor cur = database.query(DBManagement.TABLE_NAME,
            new String[] {DBManagement.Column}, "constraint="+constraint, null, null, null, null);
    return cur;
}
    //.. other methods down here which do rawQuery, QueryBuilder, etc.. 

数据库管理

public class DBManagement extends SQLiteOpenHelper{

    // .. table definitions and columns etc ..//

    public DBManagement(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);      
    }

在我的activity的onCreate方法中,我会调用datasource.open(),这样SQL连接就打开了。之后我会执行以下操作:

DataSource datasource = new DataSource();

Cursor cursor = datasource.exampleCursor(1);
startManagingCursor(cursor);

如果我导航到一个新的 Activity, 我会得到以下错误:
 06-27 21:59:14.812: E/Database(13396): close() was never explicitly called on database '/data/data/com.example.package/databases/db.db' 

如果我在onCreate的结尾添加datasource.close();,那么简单的游标适配器将无法工作,或者如果在上下文菜单上执行某个操作,则会出现数据库未打开的错误。如何处理上述情况是最佳方式?因此,我尝试了以下方法,但仍然遇到了数据库问题:
@Override
public void onBackPressed() {
    // do something on back.
    //Log.i(getClass().toString(), "onBackPressed");

    datasource.close();

    finish();
    return;
}

@Override
protected void onResume(){
    super.onResume();
    onCreate(null);
}


@Override
protected void onRestart(){
    datasource = new DataSource(this);
    datasource.open();

    filters = datasource.getFilterCursor();
    startManagingCursor(filters);

    super.onRestart();
}

@Override
protected void onPause(){
    //Log.i(getClass().toString(), "onPause");
    ((CursorAdapter) adapter).getCursor().close();
    datasource.close();
    super.onPause();
}   
@Override
protected void onStop(){
    //Log.i(getClass().toString(), "onStop");
    datasource.close();
    super.onStop();
}

我的Datasource.java类有以下内容:

public Datasource(Context context){
     dbHelper = new DBManagement(context);
}

public void open() throws SQLException {
    if( database == null ){
           database = dbHelper.getWritableDatabase();
    }
}

public void close(){
    if(dbHelper != null){
         dbHelper.close();
    }
}
5个回答

5
这其实很简单:
  • 在完成后显式关闭所有的游标(使用finally)等。
  • 不要使用startManagingCursor()
  • 将数据库包装器/助手/管理器/任何类设为单例
  • 不要明确调用close()在数据库帮助程序上。当您的进程死亡时,它将自动关闭。

另外,频繁地打开和关闭绝对是一个坏主意,因为它会带来相当大的开销。

使用加载器也是一个选择。您绝对不需要使用内容提供程序来使用加载器,但这并不像它应该那样直截了当。使用内容提供程序涉及IPC,如果您不计划将数据导出到其他应用程序,则通常过度。


“try”/“catch”/“finally” 应该放在 onCreate 中吗?在 onCreate 中访问数据库可以吗,还是需要使用其他方法? - Mike Mackintosh
try/finally 语句块通常用于使用游标的任何位置。一般来说,不要从主线程访问数据库,只要您有一个有效的上下文来创建它,调用它的确切位置并不重要。 - Nikolay Elenkov

4

如果你在onCreate中打开了数据库,那么你可以在onDestroy中关闭它。


我会说这不是一个好的方式,因为如果你在去另一个Activity之前没有完成当前Activity,那么有些Activity也可能进入暂停状态。 - Lalit Poptani
也许将其移动到onResume/onPause会更好。 - Ran
我更新了我的方法,即使在暂停/停止/返回按键内部使用close,我仍然会收到错误。 - Mike Mackintosh

4
您的问题的“理想”解决方案是过渡到Content ProvidersLoaders,并使用v4兼容库进行向后兼容。这样做解决了这个问题,因为您不再需要关心打开和关闭数据库连接,并且可以在后台执行数据库操作而不是在UI线程上执行。
它还可以使您的应用程序具备未来性,因为startManagingCursor已被弃用。目前仍然可以使用它,即使在4.1中也是如此,但它将在某个时候被删除。
我在这里有另一篇关于使用Content Providers的文章,其中涉及使用提供程序的其他原因,并提供了教程链接。
我认为Google在ContentProvider方面最大的失败之一是他们没有提供一种简单直观的构建方式。

2

我建议在从数据库中获取数据完成后立即关闭数据库连接。而且最好在onResume()onPause()内部再次打开和关闭。


现在,我应该在 onResume 中打开、执行操作,然后再关闭呢?还是只需要打开和关闭?我是否还需要手动关闭我的列表适配器 adapter?在 onResume 中,我需要重新创建它吗? - Mike Mackintosh
当您从数据库中获取数据完成后,关闭数据库会更好。 - Lalit Poptani
在上下文或列表视图中的点击或操作时,我应该重新打开数据源还是运行游标重新查询? - Mike Mackintosh
重新打开它,因为我猜测requery已经被弃用了。 - Lalit Poptani

2

我记得曾经遇到类似的问题,每当在新的活动中调用getWritableDatabase()而不关闭旧引用时,我就会重新创建错误。当我关闭SQLiteDatabase对象database和SQLiteOpenHelper对象dbHelper时,我不再收到这些错误。

public void close() {
    // Check against the database being created but not opened, close writable db
    if(database != null) {
        database.close();
        database = null;
    }

    // In case someone calls DataSource.close() more than once...
    if(dbHelper != null) {
        dbHelper.close();
        dbHelper = null;
    }
}

希望这能帮到您!

我移除了所有的managecursor调用,并手动关闭了它们,同时在我的dbhelper上调用了super.close,问题就解决了。谢谢! - Mike Mackintosh
对于我的每个活动,我在onDestroy()中清理了我的数据库……这很有效。 - James Oravec

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接