无法返回Cursor对象 - close()永远不会被称为错误

时间:2011-04-10 18:58:42

标签: java android sqlite jdbc cursor

嘿。我正在尝试将光标对象返回到我的活动,它将在SimpleCursorAdapter中使用,但我有一个 close() was never explicity called 错误。我找不到任何解决此错误的方法,而且我将认为它是非解决方案错误,LOL。

和我一起思考。

错误说:

  从未明确调用

close()   数据库   '/data/data/com.example.myapp/databases/myDB.db'

也有警告:

  

在终结器中发布声明。   请确保您明确致电   光标上的close():SELECT * FROM   contact_data ORDER BY duration desc

     

android.database.sqlite.DatabaseObjectNotClosedException:   应用程序未关闭此处打开的游标或数据库对象

错误在于此方法,即DataHandlerDB类(处理数据库)

public static Cursor selectTopCalls(Context ctx) {

        OpenHelper helper = new OpenHelper(ctx);
        SQLiteDatabase db = helper.getWritableDatabase(); // error is here

        Cursor cursor = db.query(TABLE_NAME_2, null, null, null, null, null,
                "duration desc");

        return cursor;
    }

此方法用于我的活动,通过以下方法:

public void setBasicContent() {

    listview = (ListView) findViewById(R.id.list_view); 

    Log.i(LOG_TAG, "listview " + listview);

    Cursor c = DataHandlerDB.selectTopCalls(this); // here I use the method
    startManagingCursor(c);

    adapter = new SimpleCursorAdapter(this, R.layout.list_item, c, new String[] {               
            DataHandlerDB.CONTACT_NAME_COL,
            DataHandlerDB.CONTACT_NUMBER_COL,
            DataHandlerDB.CONTACT_DURATION_COL,
            DataHandlerDB.CONTACT_DATE_COL }, new int[] {
            R.id.contact_name, R.id.phone_number, R.id.duration, R.id.date });

    Log.i(LOG_TAG, "before setAdapter");

    Toast.makeText(this, "Before setAdapter", Toast.LENGTH_SHORT).show();

    listview.setAdapter(adapter);


}

我尝试关闭此方法中的游标和数据库,但是当我这样做时,错误没有修复,并且它不会打印我的列表。

我尝试在selectValues()方法上关闭它,但是当这样做时,它说,尝试重新打开已经关闭的游标(类似)。

我还尝试在onDestroy(),onStop()中关闭游标和数据库,但它没有用。

这就是为什么我认为没有解决方案。我该怎么办?

DataHandlerDB.java ,有一个createDB()方法:

public static SQLiteDatabase createDB(Context ctx) {
        OpenHelper helper = new OpenHelper(ctx);
        SQLiteDatabase db = helper.getWritableDatabase();
        helper.onOpen(db);
        db.close();
        return db;
    }

一个名为 OpenHelper的内部类(扩展SQLiteOpenHelper)

public static class OpenHelper extends SQLiteOpenHelper {

        private final Context mContext;

        OpenHelper(Context context) {

            super(context, DATABASE_NAME, null, DATABASE_VERSION);
            this.mContext = context;

        }

        @Override
        public void onCreate(SQLiteDatabase db) {

            String[] sql = mContext.getString(
                    R.string.MyString_OnCreate).split("\n");

            db.beginTransaction();

            try {
                execMultipleSQL(db, sql);
                db.setTransactionSuccessful();
            } catch (SQLException e) {

                Log.e("Error creating tables and debug data", e.toString());
                throw e;

            } finally {
                db.endTransaction();

            }
        }

        private void execMultipleSQL(SQLiteDatabase db, String[] sql) {

            for (String s : sql) {

                if (s.trim().length() > 0) {

                    db.execSQL(s);
                }
            }

        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            /*
             * Log.w("My Database",
             * "Upgrading database, this will drop tables and recreate.");
             * db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); onCreate(db);
             */
        }

        @Override
        public void onOpen(SQLiteDatabase db) {

            super.onOpen(db);
        }

    }

所以任何人都可以帮我解决这个问题?谢谢!

1 个答案:

答案 0 :(得分:2)

不是在每个静态方法中创建新的OpenHelper,而是使用当前上下文将DataHandlerDB实例化,并让类保存由getWritableDatabase填充的变量。您正在创建由不同SQLiteOpenHelper对象创建的多个光标对象,Android不喜欢这样做。

查看此信息以获取更多信息:http://www.ragtag.info/2011/feb/1/database-pitfalls/

这就像你在做什么一样......

public class DataHandlerDB{

    public static SQLiteDatabase createDB(Context ctx) {
        OpenHelper helper = new OpenHelper(ctx);
        SQLiteDatabase db = helper.getWritableDatabase();
        ...
        return db;
    }

    public static Cursor selectTopCalls(Context ctx) {
        OpenHelper helper = new OpenHelper(ctx);
        SQLiteDatabase db = helper.getWritableDatabase(); // error is here
        ...
        return c;
    }

}

这会导致多个并发SQLiteOpenHelper个对象和多个SQLiteDatabase对象以及您当前的锁定情况。

不是进行多次静态调用,而是创建一个DataHandler类,使用一致的上下文进行实例化,然后进行正常调用(而不是静态调用):

public class DataHandlerDB{
    OpenHelper _helper;
    SQLiteDatabse _db;

    public DataHandlerDB( Context ctx ){
        _helper = new OpenHelper(ctx);
        _db = _helper.getWritableDatabase();
    }

    public SQLiteDatabase createDB() {
        ...
        return db;
    }

    public Cursor selectTopCalls() {
        ...
        return c;
    }

}

public void setBasicContent() {

    ...

    DataHandlerDB handler = new DataHandlerDB( this );
    Cursor c = handler.selectValues();  //.selectTopCalls()?

    ...
}

创建此对象时,它会持续1 OpenHelper和1 SQLiteDatabase。这可以缓解SQLite在访问数据库之前需要关闭数据库的问题。

不要忘记在活动的onDestroy方法中关闭数据库。