Почему SQLiteOpenHelper приводит к тому, что база данных IllegalArgumentException не открывается?

Во-первых, все подобные сообщения здесь не помогли.

В AsyncTask я проверяю, является ли запуск приложения самым первым запуском, это означает, что я проверяю, существует ли вообще база данных. Я хочу сделать это по запросу из таблицы. Вот что выполняется:

@Override
public List<Entity> loadAll(Entity markerEntity)
{
    Log.i(TAG, "trying to load all entities of type " + markerEntity.getTable());

    List<Entity> results = new LinkedList<Entity>();

    SQLiteDatabase db = defaultSQLiteOpenHelper.getWritableDatabase();

    Cursor cursor = db.query(markerEntity.getTable(), markerEntity.getAllColumns(), null, null, null, null, null);    
    cursor.moveToFirst();

    while (!cursor.isAfterLast())
    {
        Entity result = markerEntity.createNewInstance(cursor);
        results.add(result);

        cursor.moveToNext();
    }

    db.close();

    return results;
}

В этой строке происходит сбой с IllegalArgumentException: database not open.

SQLiteDatabase db = defaultSQLiteOpenHelper.getWritableDatabase();

Это происходит именно на методе (см. ниже) createAllTables.

В документации говорится:

Создайте и/или откройте базу данных, которая будет использоваться для чтения и записи. При первом вызове база данных будет открыта и будут вызваны onCreate(SQLiteDatabase), onUpgrade(SQLiteDatabase, int, int) и/или onOpen(SQLiteDatabase).

На мой взгляд, он должен выполнить мой DefaultSQLiteOpenhelper и создать все таблицы базы данных, но он этого не делает:

public class DefaultSQLiteOpenHelper extends SQLiteOpenHelper implements ISQLiteOpenHelper
{
    private static final String TAG = DefaultSQLiteOpenHelper.class.getSimpleName();

    private static final String DATABASE_NAME = "MY_DATABASE";
    private static final int DATABASE_VERSION = 1;

    @Inject
    public DefaultSQLiteOpenHelper(Context context)
    {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    //is called by the framework if the database doesn't exist
    @Override
    public void onCreate(SQLiteDatabase db)
    {
        Log.i(TAG, "no database found. Generating new database...");

        createAllTables(db);    
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
    {
         Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion);
//       db.execSQL("DROP TABLE IF EXISTS " + TABLE_COMMENTS); ///TODO when upgrade
//       onCreate(db);
    }

    //The order must not be changed because sqlite doesn't allow
    //table modification. That means one cannot add constraints afterwards
    //which results in creating tables in a specific order
    private final void createAllTables(SQLiteDatabase db)
    {
        db.beginTransaction();

        //create lookups at first
        db.execSQL(TableFactory.createUsage());
        db.execSQL(TableFactory.createLanguage());

        db.execSQL(TableFactory.createAccount());
        db.execSQL(TableFactory.createPreferences());
        db.execSQL(TableFactory.createLanguageUsageRel());
        db.execSQL(TableFactory.createPantry());
        db.execSQL(TableFactory.createProduct());

        //populate lookups
        db.execSQL(DataFactory.populateUsage());
        db.execSQL(DataFactory.populateLanguage());

        db.setTransactionSuccessful();
        db.endTransaction();
        db.close();
    }
}

Вопрос в том, почему он не открывает базу данных? В документации сказано, что должен. Я не принимаю никаких грязных обходных путей в качестве решения, таких как открытие базы данных вручную или что-то в этом роде. Что именно не так в моем коде? Я не могу поверить, что это ошибка в Android.

Вот трассировка стека:

05-27 17:25:50.370: I/DefaultSQLiteOpenHelper(719): база данных не найдена. Создание новой базы данных... 05-27 17:25:50.400: W/dalvikvm(719): threadid=10: поток завершается с необработанным исключением (группа=0x40015560) 05-27 17:25:50.400: E/AndroidRuntime(719 ): ФАТАЛЬНОЕ ИСКЛЮЧЕНИЕ: AsyncTask

1 05-27 17:25:50.400: E/AndroidRuntime(719): java.lang.RuntimeException: при выполнении произошла ошибка

doInBackground() 05-27 17:25:50.400: E/AndroidRuntime(719): at android.os.AsyncTask$3.done(AsyncTask.java:200) 05-27 17:25:50.400: E/AndroidRuntime(719) : в java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274) 05-27 17:25:50.400: E/AndroidRuntime(719): в java.util.concurrent.FutureTask.setException(FutureTask.java :125) 05-27 17:25:50.400: E/AndroidRuntime(719): в java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308) 05-27 17:25:50.400: E/AndroidRuntime (719): в java.util.concurrent.FutureTask.run(FutureTask.java:138) 05-27 17:25:50.400: E/AndroidRuntime(719): в java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor. java:1088) 05-27 17:25:50.400: E/AndroidRuntime(719): в java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581) 05-27 17:25:50.400: E/ AndroidRuntime(719): в java.lang.Thread.run(Thread.java:1019) 05-27 17:25:50.400: E/AndroidRuntime(719): вызвано автор: java.lang.IllegalStateException: база данных не открыта 05–27 17:25:50.400: E/AndroidRuntime (719): в android.database.sqlite.SQLiteDatabase.endTransaction(SQLiteDatabase.java:555) 05–27 17:25 :50.400: E/AndroidRuntime(719): в android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:137) 05-27 17:25:50.400: E/AndroidRuntime(719): в com.mydomain.android. base.persistence.PersistenceManager.loadAll(PersistenceManager.java:58) 05-27 17:25:50.400: E/AndroidRuntime(719): в com.mydomain.android.base.main.StartupTask.isFirstStartAfterInstallation(StartupTask.java:107 ) 05-27 17:25:50.400: E/AndroidRuntime(719): в com.mydomain.android.base.main.StartupTask.doInBackground(StartupTask.java:52) 05-27 17:25:50.400: E/AndroidRuntime (719): на com.mydomain.android.base.main.StartupTask.doInBackground(StartupTask.java:18) 05-27 17:25:50.400: E/AndroidRuntime(719): на android.os.AsyncTask$2.call (AsyncTask.java:185) 05-27 17:25:50 .400: E/AndroidRuntime(719): в java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306) 05-27 17:25:50.400: E/AndroidRuntime(719): ... еще 4


person Bevor    schedule 27.05.2013    source источник
comment
Вы можете рассмотреть возможность публикации трассировки стека.   -  person CommonsWare    schedule 27.05.2013
comment
@CommonsWare Вот оно.   -  person Bevor    schedule 27.05.2013
comment
db.close() в onCreate() неверно.   -  person laalto    schedule 27.05.2013


Ответы (1)


Во-первых, вы закрываете базу данных в createAllTables(). Никогда не закрывайте базу данных из onCreate() или onUpgrade() из SQLiteOpenHelper.

Во-вторых, ваша транзакционная логика не нужна в createAllTables(), так как onCreate() выполняется в транзакции. Это хорошо, так как логика вашей транзакции в любом случае реализована неправильно.

Чтобы прояснить этот последний комментарий, правильный способ выполнения транзакций:

try {
    db.beginTransaction();

    // SQL

    db.setTransactionSuccessful();
}
// optional catch block
finally {
    db.endTransaction();
}

Вам нужно вызвать endTransaction(), если есть исключение SQL - ваша реализация пропускает это.

person CommonsWare    schedule 27.05.2013
comment
@CommmonsWare Можно ли получить доступную для записи базу данных в AyncTask? У меня возникает соблазн сказать, что это может вызвать одновременное исключение. Не могли бы вы просветить меня на это, пожалуйста? - person user2336315; 27.05.2013
comment
@CommmonsWare Спасибо, какая глупая ошибка. :) Кстати: вы имеете в виду, что неправильно реализованный способ обработки транзакций - это неправильный способ (несмотря на этот пример, где мне это не нужно)? (Я делаю что-то подобное в своем методе сохранения). - person Bevor; 27.05.2013
comment
@ user2336315: Можно ли получить доступную для записи базу данных в AyncTask? -- это не только разрешено, но и в значительной степени обязательно, так как getWriteableDatabase() может выполнять значительные дисковые операции ввода-вывода, если ему необходимо создать или обновить базу данных. У меня возникает соблазн сказать, что это может вызвать одновременное исключение. -- как вы видите, посмотрев исходный код, он синхронизирует эту работу. - person CommonsWare; 27.05.2013
comment
@CommonsWare Спасибо, приятно видеть, что я уже делаю это в своем методе сохранения таким образом. - person Bevor; 27.05.2013