「SQLite」データベースの同時アクセスセキュリティの問題を効率的に解決するには、この1編だけで十分です.

9843 ワード

「SQLite」データベースの同時アクセスセキュリティの問題を効率的に解決するには、この1編だけで十分です.
 

Concurrent database access


この文書は次のとおりです.https://dmytrodanylyk.com/articles/concurrent-database/
Android Devにとって、SQLiteに関する操作はよくありますが、コンソールが赤くなったことを経験したことがあるよりも、SQLiteはスレッドが安全なのか疑問に思っています.
OKくだらないことは言わないで、私たちは⬇️

ダイレクトスタート


まず、SQLiteHelperクラスを実装したとします。以下に示します。

public class DatabaseHelper extends SQLiteOpenHelper { ... }

2つのサブスレッドにSQLiteにそれぞれいくつかのデータを書き込むようになりました.
 // Thread 1
 Context context = getApplicationContext();
 DatabaseHelper helper = new DatabaseHelper(context);
 SQLiteDatabase database = helper.getWritableDatabase();
 database.insert(…);
 database.close();

 // Thread 2
 Context context = getApplicationContext();
 DatabaseHelper helper = new DatabaseHelper(context);
 SQLiteDatabase database = helper.getWritableDatabase();
 database.insert(…);
 database.close();

でしょ?OKそうで大丈夫です.
では、runを注文すると、gio~あなたのlogcatに次のようなプレゼントが届きます.
android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

いったいどういうことなのか。


エラーを分析してみると、SQLiteHelperを作成するたびにデータベースにリンク操作が行われているためです.この場合、実際に異なるリンクからデータベースへの書き込みを試みてみると、失敗は必然的です.
さらに異なるスレッドでデータベースを読み書き操作を含む任意の使用を行うには、同じ接続を使用していることを確認する必要があります.
はい、それでは今問題が分かりました.次に、単一のパターンクラスを作成します.DatabaseManagerは、単一のDatabaseManagerオブジェクトを作成および返すために使用されます.
ps一部の学友は私に何が単例のモードなのかと聞いて、私はわざわざこのブログを書いて説明して、単例のモード-全局の利用可能なcontextの対象、この1編は十分でコードワードは私にほめてくれませんありがとうございます
public class DatabaseManager {

    private static DatabaseManager instance; private static SQLiteOpenHelper mDatabaseHelper; public static synchronized void initializeInstance(SQLiteOpenHelper helper) { if (instance == null) { instance = new DatabaseManager(); mDatabaseHelper = helper; } } public static synchronized DatabaseManager getInstance() { if (instance == null) { throw new IllegalStateException(DatabaseManager.class.getSimpleName() + " is not initialized, call initialize(..) method first."); } return instance; } public synchronized SQLiteDatabase getDatabase() { return mDatabaseHelper.getWritableDatabase(); } }

次に、戻って修正する前のコードを変更します.結果は次のようになります.
// In your application class
DatabaseManager.initializeInstance(new DatabaseHelper());

// Thread 1
DatabaseManager manager = DatabaseManager.getInstance();
SQLiteDatabase database = manager.getDatabase()
database.insert(…);
database.close();

// Thread 2
DatabaseManager manager = DatabaseManager.getInstance();
SQLiteDatabase database = manager.getDatabase()
database.insert(…);
database.close();

ロジックは以前よりも明確で、コードの冗長性も少なくなりました.コードを走っていると、もう一つのcacheが届きます.
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase

慌てないでください.私たちはよく分析して間違いを報告して、私たちは発見しました:単例モードの使用は私たちを保証して、スレッドの1、2の「Thread 1、Thread 2」で唯一のSQLiteHelperオブジェクトしか得られませんが、この時問題が来て、私たちがスレッドの1「Thread 1」を実行した時、私たちのdatabase.close();はすでに私たちのためにデータベースへの接続を閉じました.しかし、同時に私たちのスレッド2「Thread 2」はSQLiteHelperへの参照を維持しています.そのため、私たちはIllegalStateExceptionのエラーを受け取りました.
したがって、SQLiteHelperを使用する人がいない場合、接続を切断することを保証する必要があります.

SQLIiteHelperが誰も使用していないときに接続を切断することを保証します。


この問題の解決stackoveflowでは、SQLiteHelperの接続を決して切らないことをお勧めしますが、logcatでは次のような出力が得られます.
Leak found
Caused by: java.lang.IllegalStateException: SQLiteDatabase created and never closed

だから、私はあなたがこの方法を使うことをとてもお勧めしません.この問題を解決するためにカウンタの概念を導入した.

標準サンプル


次の方法で、データベース接続のオン/オフの問題をカウンタで完全に解決します.
public class DatabaseManager {

    private AtomicInteger mOpenCounter = new AtomicInteger(); private static DatabaseManager instance; private static SQLiteOpenHelper mDatabaseHelper; private SQLiteDatabase mDatabase; public static synchronized void initializeInstance(SQLiteOpenHelper helper) { if (instance == null) { instance = new DatabaseManager(); mDatabaseHelper = helper; } } public static synchronized DatabaseManager getInstance() { if (instance == null) { throw new IllegalStateException(DatabaseManager.class.getSimpleName() + " is not initialized, call initializeInstance(..) method first."); } return instance; } public synchronized SQLiteDatabase openDatabase() { if(mOpenCounter.incrementAndGet() == 1) { // Opening new database mDatabase = mDatabaseHelper.getWritableDatabase(); } return mDatabase; } public synchronized void closeDatabase() { if(mOpenCounter.decrementAndGet() == 0) { // Closing database mDatabase.close(); } } }

スレッドでは次のように使用できます.
SQLiteDatabase database = DatabaseManager.getInstance().openDatabase();
database.insert(...);
// database.close(); Don't close it directly!
DatabaseManager.getInstance().closeDatabase(); // correct way

データベースを使用する必要がある場合は、DatabaseManagerのopenDatabase()メソッドを呼び出すだけです.この方法では、データベースが何度も「開かれた」mOpenCounterオブジェクトを記録する方法があります.1に等しい場合は、データベースを使用するために新しいデータベース接続を作成する必要があります.そうしないと、データベースが使用されていることを示します.
同様にcloseDatabase()メソッドでも発生します.このメソッドを呼び出すたびに、mOpenCounterオブジェクトが1つ減少します.0に減らすと、このデータベースの接続を閉じます.
完璧、最後:
  • 今、あなたのデータベースを好きなように使用することができます.そして、スレッドが安全だと信じることができます.
  • もちろん多くの学生はデータベースの使用に対して、まだ多くの疑問を持っていて、私は後期にデータベースの使用に対して、一連の総括をして、興味があって引き続き注目することができます_yuanhaoのプログラミング世界
  • 関連記事


    誰もが学ばなければならない画像圧縮の究極の奥義は、AndroidプログラムOOMAndroidを効果的に解決し、あなたのRoomをRxJavaの追い風車に乗せて繰り返しのコードからViewModelとViewModelProviderを解放します.Factory:ViewModelの作成者単例モード-グローバルで利用可能なcontextオブジェクト、この1編で十分スケールジェスチャーScaleGestureDetectorソースコード解析、この1編で十分Android属性アニメーションフレームワークObjectAnimator、ValueAnimator、この1編で十分です.これ以上Viewできないアニメーションフレームワークを見終わって、私はひざまずいて洗濯板を見て、このGestureDetectorジェスチャー検査を見て、私はひざまずいて洗濯板をこすります!Androidカスタムコントロール-時計盤AndroidのアップグレードカスタムView Groupカスタムレイアウトを描く

    ようこそyuanhaoのブログ園!