Використання шаблону проектування Singleton для SQLiteDatabase


77

Я досить новачок на Android, і я працюю над простим додатком, щоб отримати базовий досвід. Мій додаток досить простий і, серед іншого, складається з приймача мовлення та деяких видів діяльності. Обидва компоненти використовують єдину базу даних, тому теоретично може статися так, що обидва намагаються отримати доступ до бази даних одночасно.

В даний час я просто створюю екземпляр об'єкта db (який є допоміжним класом SQLite db) кожного разу, коли мені це потрібно, і виконую необхідні операції: запит, вставка тощо.

З того, що я читав тут і в деяких інших документах, проблема полягає в отриманні винятку "db locked" у разі одночасного доступу до db, тому кращим підходом буде наявність одного екземпляра цього об'єкта db, тому всі компоненти постійно використовують одне і те ж з'єднання db.

Чи правильні вищезазначені міркування? Чи був би одиночка тоді достатньо хорошим рішенням для цього? Я знаю, що деякі пуристи можуть висловитись проти, але зауважте, що це досить проста програма, тому я можу дозволити собі робити те, чого не робив би в інших випадках.

Інакше, що було б кращим варіантом? Я читав про використання постачальника вмісту, але для цього було б занадто багато, крім того, що я не зацікавлений ділитися даними з іншими видами діяльності. Я справді прочитав цю публікацію і знайшов її досить корисною.

Відповіді:


101

Клацніть тут, щоб побачити мою публікацію в блозі на цю тему.


Ось зразок коду, який ілюструє три можливі підходи. Це дозволить отримати доступ до бази даних протягом усієї програми.

Підхід №1: мати `SQLiteOpenHelper` статичним членом даних

Це не повне впровадження, але воно повинно дати вам гарне уявлення про те, як правильно розробляти DatabaseHelperклас. Статичний фабричний метод гарантує, що в будь-який момент часу існує лише один екземпляр 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: абстрагуйте базу даних SQLite за допомогою `ContentProvider`

Я б запропонував такий підхід. З одного боку, для нового CursorLoaderкласу потрібні ContentProviders, тому, якщо ви хочете реалізувати Activity або Fragment LoaderManager.LoaderCallbacks<Cursor>за допомогою CursorLoader(чим я пропоную скористатися, це чарівно!), Вам потрібно буде застосувати a ContentProviderдля вашого додатка. Крім того, вам не потрібно турбуватися про створення помічника бази даних Singleton за допомогою ContentProviders. Просто зателефонуйте getContentResolver()з Activity, і система подбає про все для вас (іншими словами, немає необхідності розробляти шаблон Singleton, щоб запобігти створенню декількох екземплярів).

Сподіваюся, це допомагає!


Алекс, я використовую твій підхід (№2) і люблю його простоту. Але нещодавно я помітив проблему. Цікаво, чи можете ви допомогти мені у вирішенні цієї проблеми: stackoverflow.com/questions/10972719/... До речі, я щойно помітив, що я використовую статичний mCxt (забув чому). Цікаво, чи це пов’язано з моєю проблемою
Michał K

Наведене посилання порушено ... ось оновлене: android-developers.blogspot.com/2009/01/…
Алекс Локвуд,

Ось обгортка, яку я написав / використовую, що полегшує використання SQLite добре в контексті android - SqlDb для Android
kashif

1
Дякую @AlexLockwood, хоча підхід №1 для мене цілком логічний, я думав, що його слід використовувати лише ContentProviderпід час спільного доступу до бази даних з іншим додатком?
ericn

2
Чи не було б гарною ідеєю зробити getInstanceсинхронізацію?
Matthew Mitchell

22

Я ніколи не читав про використання синглтона для доступу до баз даних на android. Не могли б ви надати посилання на це.

У своїх програмах я використовую прості об'єкти dbhelper, а не одиночні, я думав, що це більше робота движка sql, щоб переконатися, що db не блокується, а не робота ваших класів Android, і це працює досить добре для мого найбільшого додатка, який середнього розміру.

Оновлення №1: дивлячись на посилання, яке ви дали, схоже, проблема полягає зовсім не у використанні різних екземплярів файлу dbhelper. Навіть окремий екземпляр може зіткнутися з проблемами доступу до баз даних: проблема походить від одночасного доступу. Отже, єдиним способом забезпечити належний доступ до бази даних різними потоками є використання простих механізмів синхронізації потоків ( synchronizedметодів або блоків), і це майже нічого спільного з використанням одиночного.

Оновлення №2: друге посилання, яке ви надаєте, чітко показує, що вони потребують одномісних об’єктів dbhelper у разі одночасного запису декількох потоків у базу даних. Це може статися, якщо ви виконуєте SQL-операції (вставки / оновлення / видалення), наприклад, з AsyncTasks. У цьому випадку одиночний об'єкт dbhelper просто розміщує всі операції sql у якомусь конвеєрі та виконує їх у порядку.

Це рішення може бути простішим у реалізації, ніж використання належної синхронізації потоків із використанням синхронізованих методів у Java. Насправді, я вважаю, що десь в документації для Android слід більше наголошувати на цій проблемі, і можна заохочувати використання єдиного помічника db.

Дякую за це приємне запитання та подальші дії.


Спасибі Стефане. Статті, які я використав як основу, такі: stackoverflow.com/questions/2647542/… stackoverflow.com/questions/4302286/… З повагою, Дан
Дан

Проблема того, що ви коментуєте, полягає в тому, що (AFAIK) використання різних підключень до однієї бази даних є рецептом невдачі. Таким чином, використання синглтона.
Dan

Дивіться цю тему, яка, схоже, підтримує мою теорію: stackoverflow.com/questions/2493331/…
Дан,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.