回答:
私はそれを常に開いたままにし、onStop
またはなどのライフサイクルメソッドで閉じますonDestroy
。こうすることで、データベースを使用する前に毎回を呼び出すisDbLockedByCurrentThread
かisDbLockedByOtherThreads
、単一のSQLiteDatabase
オブジェクトでデータベースがすでに使用されているかどうかを簡単に確認できます。これにより、データベースに対する複数の操作が防止され、潜在的なクラッシュからアプリケーションが保存されます
したがって、シングルトンでは、次のようなメソッドを使用して単一のSQLiteOpenHelper
オブジェクトを取得できます。
private SQLiteDatabase db;
private MyDBOpenHelper mySingletonHelperField;
public MyDBOpenHelper getDbHelper() {
db = mySingletonHelperField.getDatabase();//returns the already created database object in my MyDBOpenHelper class(which extends `SQLiteOpenHelper`)
while(db.isDbLockedByCurrentThread() || db.isDbLockedByOtherThreads()) {
//db is locked, keep looping
}
return mySingletonHelperField;
}
オープンヘルパーオブジェクトを使用する場合は常に、このゲッターメソッドを呼び出します(スレッド化されていることを確認してください)。
シングルトンの別のメソッドは次のとおりです(上記のゲッターを呼び出そうとする前に毎回呼び出されます):
public void setDbHelper(MyDBOpenHelper mySingletonHelperField) {
if(null == this.mySingletonHelperField) {
this.mySingletonHelperField = mySingletonHelperField;
this.mySingletonHelperField.setDb(this.mySingletonHelperField.getWritableDatabase());//creates and sets the database object in the MyDBOpenHelper class
}
}
シングルトンでデータベースを閉じることもできます。
public void finalize() throws Throwable {
if(null != mySingletonHelperField)
mySingletonHelperField.close();
if(null != db)
db.close();
super.finalize();
}
アプリケーションのユーザーが多くのデータベース相互作用を非常に迅速に作成できる場合は、上記で示したようなものを使用する必要があります。しかし、データベースの相互作用が最小限であれば、心配する必要はなく、毎回データベースを作成して閉じるだけです。
現在のところ、データベースが別のスレッドによってロックされているかどうかを確認する必要はありません。すべてのスレッドでシングルトンSQLiteOpenHelperを使用している間は安全です。isDbLockedByCurrentThread
ドキュメントから:
このメソッドの名前は、データベースへのアクティブな接続が存在することにより、スレッドがデータベースに対して実際のロックを保持していたことを意味していました。現在、特定の操作を実行するためのデータベース接続を取得できない場合、スレッドはブロックする可能性がありますが、本当の「データベースロック」はなくなりました。
isDbLockedByOtherThreads
APIレベル16から廃止されました。
質問について:
データベースマネージャーはシングルトンで、初期化時にデータベースへの接続を開いています。
「opening DB」と「opening a connection」を分ける必要があります。SQLiteOpenHelper.getWritableDatabase()は、開かれたDBを提供します。ただし、内部で行われるため、接続を制御する必要はありません。
誰かが私のクラスを呼び出してデータベースを操作するときにすでに開いているように、データベースをずっと開いたままにしても安全です。
はい、そうです。トランザクションが適切に閉じられている場合、接続はハングしません。GCがファイナライズすると、DBも自動的に閉じられることに注意してください。
または、各アクセスが必要になる前と後にデータベースを開いて閉じる必要があります。
SQLiteDatabaseインスタンスを閉じても、接続を閉じる以外に大きな効果はありませんが、現時点でいくつかの接続がある場合、これは開発者の悪いことです。また、SQLiteDatabase.close()の後、SQLiteOpenHelper.getWritableDatabase()は新しいインスタンスを返します。
それをずっと開いたままにしておくことに害はありますか?
いいえ、ありません。また、たとえばActivity.onStop()などの無関係な瞬間およびスレッドでDBを閉じると、アクティブな接続が閉じられ、データが不整合な状態になる可能性があることにも注意してください。
Android 8.1には次のSQLiteOpenHelper.setIdleConnectionTimeout(long)
方法があります。
SQLite接続が閉じられ、プールから削除されるまでのアイドル状態を許可される最大ミリ秒数を設定します。
パフォーマンスの観点から、最適な方法は、アプリケーションレベルでSQLiteOpenHelperの単一インスタンスを維持することです。データベースを開くことは、コストがかかる可能性があり、ブロッキング操作であるため、メインスレッドやアクティビティライフサイクルメソッドでは実行しないでください。
データベースが使用されていない場合、setIdleConnectionTimeout()メソッド(Android 8.1で導入)を使用してRAMを解放できます。アイドルタイムアウトが設定されている場合、非アクティブ状態が一定期間続いた後、つまりデータベースにアクセスしなかった場合に、データベース接続が閉じられます。新しいクエリが実行されると、接続はアプリに対して透過的に再開されます。
それに加えて、アプリは、バックグラウンドになったとき、またはonTrimMemory()などでメモリプレッシャーを検出したときに、releaseMemory()を呼び出すことができます。
独自のアプリケーションコンテキストを作成し、そこからデータベースを開いて閉じます。そのオブジェクトには、接続を閉じるために使用できるOnTerminate()メソッドもあります。私はまだ試していませんが、より良いアプローチのようです。
@binnyb:finalize()を使用して接続を閉じるのは好きではありません。うまくいくかもしれませんが、Javaのfinalize()メソッドでコードを書くことは私が理解していることから、悪い考えです。
onTerminate
、本番環境で呼び出されることはありません- developer.android.com/reference/android/app/...