13

私は sqlite db を使用しており、Alex LockWood のコードを使用してSQLite データベースを正しく管理しています。

それは非常にうまく機能しますが、時々エラーが発生しました"java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed." ここに完全なエラーがあります:

02-20 16:37:21.385: W/dalvikvm(25730): threadid=13: thread exiting with uncaught exception (group=0x41c122a0)
02-20 16:37:21.390: E/AndroidRuntime(25730): FATAL EXCEPTION: Timer-0
02-20 16:37:21.390: E/AndroidRuntime(25730): java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteConnectionPool.throwIfClosedLocked(SQLiteConnectionPool.java:963)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteConnectionPool.waitForConnection(SQLiteConnectionPool.java:678)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteConnectionPool.acquireConnection(SQLiteConnectionPool.java:349)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteSession.acquireConnection(SQLiteSession.java:894)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:834)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:143)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at com.uit.pokemon.DatabaseHandler.getStadiumStatusById(DatabaseHandler.java:533)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at playground.RoomActivity.checkTable(RoomActivity.java:276)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at playground.RoomActivity$6.run(RoomActivity.java:321)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at java.util.Timer$TimerImpl.run(Timer.java:284)
02-20 16:37:21.460: I/timertask cancel(25730): canceled

そして、エラーの原因となるコードは次のとおりです。

public int getStadiumStatusById(int dataStadiumId){
        SQLiteDatabase db = this.getReadableDatabase();
        Cursor cur = db.rawQuery("SELECT " + keyStadiumId + " as _id, "
                + keyRoomName + ", " + keyP1Name + ", " + keyP1PokemonName
                + ", " + keyP1PokemonLevel + ", " + keyP1PokemonHp + ", "
                + keyP2Name + ", " + keyP2PokemonName + ", "
                + keyP2PokemonLevel + ", " + keyP2PokemonHp + ", "
                + keyTimeCreate + ", " + keyStadiumStatus + " from "
                + tbl_stadium + " WHERE " + keyStadiumId + " = " + "'"
                + dataStadiumId + "'", new String[] {});

        int stadiumStatus = 0;
        if(cur.getCount()>0)
        {
        cur.moveToFirst();
        stadiumStatus = cur.getInt(11);
        }
        db.close();
        cur.close();
        return stadiumStatus;
    }

私は何時間もグーグルを試みましたが、結果はありませんでした。修正するのを手伝ってください。どんな助けでも大歓迎です。ありがとうございました!

4

6 に答える 6

0

私もこの問題に取り組んでいます。原因はわかっているので、専門家の意見を聞きたいです。私はかなりの数の仮定を行い、証拠に裏打ちされていないいくつかの結論に達します....

「return StadiumStatus」行で例外がスローされたと思います。上記の数行で、この変数を Cursor のメンバーに割り当てます。カーソル内にのみ存在する int 変数を指すために、実際に StadiumStatus という名前を割り当てていると思います。もう 1 つの可能性 (ここでの論理の大きな飛躍) は、Cursor.getInt() によって返されるアイテムが、実際には開いているデータベース (開いているためメモリに存在する) 内のアイテム (メソッド?) へのポインターであるということです。 Cursor やデータベースを閉じてから StadiumStatus を返そうとすると、int は Cursor 内のアドレスまたは Cursor によって渡されたアドレス指しそれをたどろうとし、閉じた Cursor または閉じた Database (1 つ) で行き止まりになります。これらのうち、あなたが描画している「データプール」です。)

私が正しければ、どちらかの最良の修正は次のものを置き換えることだと思います:

"stadiumStatus = cur.getInt(11);"

と:

"stadiumStatus = new Integer(cur.getInt(11));"

これにより、vm が簡単に int にキャストできるメモリ内に新しい Integer が作成され、アクセシビリティの従来のスコープのみに依存します。

この答えは理にかなっていますか?質問者はこの問題をかなり前に解決していると思いますが、スタック オーバーフローに関する質問に回答したと言えたら幸いです (または、なぜ私が間違っているのかを教えてもらい、カーソルと DB の理解を深めてもらいたいと思います)。

編集: これらの答えは、私が正しいかもしれないことを意味します:
Android エラー: 接続プールが閉じられているため、この操作を実行できません
データベースが閉じられた後、カーソルはまだ生きていますか?
2 番目の回答で、非常に評判の良いユーザー DeeV は、Cursors を「永続的な接続」と呼んでおり、DB から返された値の単純なコレクションではないことを暗示しています。

于 2014-02-28T08:37:58.210 に答える
0

クラスによって開かれたデータベースは、SQLiteOpenHelperクラスが存続している間、開いたままになります。

したがって、データベースを開いたり閉じたりする必要はありません。実際、複数のスレッドで使用すると、正しくありません。

私のアプリは、しばらくの間データベース エラーが発生していません。単一のヘルパー クラスがあります。これはシングルトンである必要があるため、SqliteDatabase の異なるインスタンスが同時にファイルを変更しようとはしません。

また、以前に発生したクラッシュのいくつかは、カーソルとクエリの両方が sqlite クエリを呼び出すことができることを示しているため、データベースへの同時読み取りおよび書き込みアクセスを許可しても意味がありません。

このため、ヘルパーにリーダー/ライター ロックを追加し、このパターンを使用します...

    public static class MyDbHelper extends SQLiteOpenHelper {
        private MyDbHelper (Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }
        public static MyDbHelper mInstance;
        private static Object mObj = new Object();
        public static MyDbHelper getInstance( Context context ){
            synchronized (mObj ){
                if( mInstance == null ){
                    mInstance = new MyDbHelper ( context );
                }
            }
            return mInstance;
        }
        private ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
        public Lock readLock() {
            return mLock.readLock();
        }
        public Lock writeLock() {
            return mLock.writeLock();
        }

クラスの使い方はこんな感じ…。

    MyStorageContract.MyDbHelper helper = MyStorageContract.MyDbHelper.getInstance(this);
    SQLiteDatabase db = helper.getReadableDatabase();
    helper.readLock().lock();
    try {
        String[] columns = {
                MyStorageContract.Libraries.COLUMN_NAME_LIBRARY_ID,
                MyStorageContract.Libraries.COLUMN_NAME_LIBRARY_DIR,
                MyStorageContract.Libraries.COLUMN_NAME_LIBRARY_SOMETHING,
        };
        String result = null;
        Cursor c = db.query(MyStorageContract.Libraries.TABLE_NAME, columns, null, null, null, null, null, "1");
        int idxDir = c.getColumnIndex(MyStorageContract.Libraries.COLUMN_NAME_LIBRARY_DIR);
        int idxRoot = c.getColumnIndex(MyStorageContract.Libraries.COLUMN_NAME_LIBRARY_SOMETHING);
        int idxId = c.getColumnIndex(MyStorageContract.Libraries.COLUMN_NAME_LIBRARY_ID);
        for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
            result = c.getString(idxDir);
        }
        c.close();
        return result;
    }
    finally {
        helper.readLock().unlock();
    }
于 2018-01-31T22:41:28.063 に答える
-6

電話

db.open()

この行の後:

SQLiteDatabase db = this.getReadableDatabase();

ただし、個人的には正確にはわかりませんがthis.getReadableDatabase()、独自のデータベースアダプターを作成するだけです。詳細については、次のリンクを参照してください: http://www.vogella.com/articles/AndroidSQLite/article.html

于 2013-09-27T05:28:02.723 に答える