web-dev-qa-db-ja.com

SQLiteDatabaseにシングルトンデザインパターンを使用する

私はAndroidの初心者であり、基本的な経験を積むために簡単なアプリケーションに取り組んでいます。私のアプリは非常にシンプルで、特に放送受信機といくつかのアクティビティで構成されています。両方のコンポーネントは単一のデータベースを使用するため、理論的には両方が同時にデータベースにアクセスしようとする可能性があります。

現在、必要なたびにdbオブジェクト(SQLite dbヘルパークラス)をインスタンス化し、必要な操作(クエリ、挿入など)を実行しています。

私がここや他のドキュメントで読んだことから、これはdbが同時にアクセスされた場合に「dbロック」例外を取得するという問題があるので、より良いアプローチはこのdbオブジェクトの単一インスタンスを持つことですコンポーネントは常に同じdb接続を使用します。

上記の推論は正しいですか?シングルトンは、このための十分なソリューションでしょうか?一部の純粋主義者はこれに反対するかもしれないことを知っていますが、これはかなり単純なアプリケーションなので、他の場合にはしないことをする余裕があることに注意してください。

そうでなければ、より良いオプションは何でしょうか?コンテンツプロバイダーの使用について読んだことがありますが、これには多すぎます。さらに、他のアクティビティとデータを共有することに興味がありません。私は確かにこれを読んだ post .

70
Dan

このテーマに関する私のブログ投稿を見るにはここをクリックしてください。


3つの可能なアプローチを示すサンプルコードを次に示します。これらにより、アプリケーション全体でデータベースにアクセスできます。

アプローチ1:「SQLiteOpenHelper」を静的データメンバーにする

これは完全な実装ではありませんが、DatabaseHelperクラスを正しく設計する方法についての良いアイデアを提供するはずです。静的ファクトリメソッドは、常に1つのDatabaseHelperインスタンスのみが存在することを保証します。

_/**
 * create custom DatabaseHelper class that extends SQLiteOpenHelper
 */
public class DatabaseHelper extends SQLiteOpenHelper { 
    private static DatabaseHelper mInstance = null;

    private static final String DATABASE_NAME = "databaseName";
    private static final String DATABASE_TABLE = "tableName";
    private static final int DATABASE_VERSION = 1;

    private Context mCxt;

    public static DatabaseHelper getInstance(Context ctx) {
        /** 
         * use the application context as suggested by CommonsWare.
         * this will ensure that you dont accidentally leak an Activitys
         * context (see this article for more information: 
         * http://Android-developers.blogspot.nl/2009/01/avoiding-memory-leaks.html)
         */
        if (mInstance == null) {
            mInstance = new DatabaseHelper(ctx.getApplicationContext());
        }
        return mInstance;
    }

    /**
     * constructor should be private to prevent direct instantiation.
     * make call to static factory method "getInstance()" instead.
     */
    private DatabaseHelper(Context ctx) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        this.mCtx = ctx;
    }
}
_

アプローチ#2: `ContentProvider`でSQLiteデータベースを抽象化する

これは私が提案するアプローチです。たとえば、新しいCursorLoaderクラスにはContentProvidersが必要です。そのため、アクティビティまたはフラグメントでCursorLoaderを使用して_LoaderManager.LoaderCallbacks<Cursor>_を実装する場合(これを利用することをお勧めします) 、それは魔法です!)、アプリケーションにContentProviderを実装する必要があります。さらに、ContentProviderを使用してシングルトンデータベースヘルパーを作成することについて心配する必要はありません。アクティビティからgetContentResolver()を呼び出すだけで、システムがすべてを処理します(つまり、複数のインスタンスが作成されるのを防ぐためにシングルトンパターンを設計する必要はありません)。

お役に立てれば!

96
Alex Lockwood

私は、シングルトンを使用してAndroidのデータベースにアクセスすることを読んだことがありません。それに関するリンクを提供してくれませんか。

私のアプリでは、シングルトンではなくシンプルなdbhelperオブジェクトを使用しています。これは、Androidクラスの仕事ではなく、dbがロックされないようにするSQLエンジンの仕事だと考えていました。中規模の私の最大のアプリではかなりうまく機能します。

更新#1:あなたが与えたリファレンスを見ると、問題はdbhelperの異なるインスタンスの使用に関するものではないようです。単一のインスタンスでも、データベースへのアクセスに問題が発生する可能性があります。問題は同時アクセスに起因します。したがって、異なるスレッドによるデータベースへの適切なアクセスを保証する唯一の方法は、単純なスレッド同期メカニズム(synchronizedメソッドまたはブロック)を使用することであり、シングルトンの使用とはほとんど関係ありません。

更新#2:2つ目のリンクは、複数のスレッドがdbに同時に書き込む場合にシングルトンdbhelperオブジェクトが必要であることを明確に示しています。これは、たとえばAsyncTasksからsql操作(挿入/更新/削除)を行うと発生する可能性があります。その場合、シングルトンオブジェクトdbhelperは、すべてのsql操作を何らかのパイプラインに配置し、それらを順番に実行します。

このソリューションは、Javaの同期メソッドを使用して適切なスレッド同期を使用するよりも簡単に実装できます。実際、Androidこの問題に関するドキュメントのどこかにもっと強調する必要があると思います。シングルトンdbヘルパーの使用を奨励できます。

この素敵な質問とフォローアップをありがとう。

20
Snicolas