41

サブクラス化されたアプリケーションのメンバーとして SQLiteOpenHelper の単一のインスタンスを持ち、SQLiteDatabase のインスタンスを必要とするすべてのアクティビティが 1 つのヘルパーからそれを取得することは問題ないでしょうか?

4

5 に答える 5

49

ここをクリックして、この件に関する私のブログ投稿をご覧ください。


CommonsWare は正しいです (いつものように)。彼の投稿を拡張して、3 つの可能なアプローチを示すサンプル コードを次に示します。これらにより、アプリケーション全体でデータベースへのアクセスが許可されます。

アプローチ #1: `Application` をサブクラス化する

アプリケーションがそれほど複雑ではないことがわかっている場合 (つまり、 のサブクラスが 1 つしかないことがわかっている場合Application)、 のサブクラスを作成し、Applicationメインのアクティビティでそれを拡張することができます。これにより、データベースの 1 つのインスタンスがアプリケーションのライフ サイクル全体で実行されます。

public class MainApplication extends Application {

    /**
     * see NotePad tutorial for an example implementation of DataDbAdapter
     */
    private static DataDbAdapter mDbHelper;

    /**
     * Called when the application is starting, before any other 
     * application objects have been created. Implementations 
     * should be as quick as possible...
     */
    @Override
    public void onCreate() {
        super.onCreate();
        mDbHelper = new DataDbAdapter(this);
        mDbHelper.open();
    }

    public static DataDbAdapter getDatabaseHelper() {
        return mDbHelper;
    }
}

アプローチ #2: `SQLiteOpenHelper` を静的データ メンバーにする

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

/**
 * 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://developer.android.com/resources/articles/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;
    }
}

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

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

それが役立つことを願っています!

于 2012-01-17T02:36:19.400 に答える
41

単一のSQLiteOpenHelperインスタンスを持つことは、スレッド化の場合に役立ちます。すべてのスレッドが共通の を共有するためSQLiteDatabase、操作の同期が提供されます。

ただし、のサブクラスは作成しませんApplication。あなたのSQLiteOpenHelper. どちらのアプローチでも、どこからでもアクセスできるものを提供します。ただし、のサブクラスは1 つしかないため、の他のサブクラスApplicationを使用することはより困難になります(たとえば、GreenDroid には 1 つの IIRC が必要です)。静的データ メンバーを使用すると、それが回避されます。ただし、この static (コンストラクター パラメーター) をインスタンス化するときに を使用して、他の をリークしないようにしてください。ApplicationApplication ContextSQLiteOpenHelperContext

SQLiteOpenHelperまた、複数のスレッドを扱っていない場合は、コンポーネントごとに 1 つのインスタンスを使用するだけで、メモリ リークの問題を回避できます。ただし、実際には、複数のスレッド (たとえば、 a ) を処理する必要Loaderがあるため、この推奨事項は、いくつかの本に見られるような単純なアプリケーションにのみ関連します... :-)

于 2012-01-17T01:24:56.210 に答える
7

複数のスレッドが同じ sqlite データベースを開いたり閉じたりする Android アプリケーション用の拡張 SQLiteOpenHelper である MultiThreadSQLiteOpenHelper を作成しました。

close メソッドを呼び出す代わりに、スレッドはデータベースを閉じるように要求し、閉じられたデータベースに対してスレッドがクエリを実行できないようにします。

各スレッドがクローズを要求した場合、クローズが実際に実行されます。各アクティビティまたはスレッド (UI スレッドとユーザー スレッド) は、再開時にデータベースでオープン呼び出しを実行し、一時停止または終了時にデータベースを閉じるように要求します。

ここで入手可能なソースコードとサンプル: https://github.com/d4rxh4wx/MultiThreadSQLiteOpenHelper

于 2012-03-15T10:15:33.203 に答える