Исключение SQLite: база данных заблокирована проблема - PullRequest
11 голосов
/ 05 октября 2011

У меня проблема с базой данных SQLite в приложении для Android.Кажется, что это случается очень часто, и я не могу воспроизвести его, но это отчет, который дает Android Market.

По сути, сначала у меня появляется заставка, которая начинается с проверки наличия всех необходимых данных в базе данных.,Если нет, он установит данные в асинхронном потоке и закроет соединение с БД.

Тогда и только тогда можно будет запустить основное действие, которое также открывает и читает / записывает базу данных.

Этот код выполняется из метода onCreate действия экрана-заставки:

dbWord = new WordDBAdapter(this, myUI);
dbWord.open();
if (dbWord.isDataInstalled()) {
    dbWord.close();
    databaseInstalled = true;
    clickToStartView.setText(myUI.PRESS_TO_START);
    clickToStartView.startAnimation(myBlinkAnim);
} else {
    // If not, try to install in asynchronous thread
    progressDialog = new ProgressDialog(this);
    progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    progressDialog.setMessage(myUI.INSTALLING_DB);
    progressDialog.setCancelable(false);
    new InstallDBData().execute("");
}

Код асинхронного потока:

private class InstallDBData extends AsyncTask<String, Integer, Integer> {

    @Override
    protected Integer doInBackground(String... arg0) {
        progressDialog.setProgress(0);
        dbWord.installData(0, getApplicationContext());
        progressDialog.setProgress(20);
        dbWord.installData(1, getApplicationContext());
        progressDialog.setProgress(40);
        dbWord.installData(2, getApplicationContext());
        progressDialog.setProgress(60);
        dbWord.installData(3, getApplicationContext());
        progressDialog.setProgress(80);
        dbWord.installData(4, getApplicationContext());
        progressDialog.setProgress(100);
        return 1;
    }

    protected void onPreExecute() {
        progressDialog.show();
    }

    protected void onPostExecute(Integer x) {
        dbWord.close();
        progressDialog.hide();
        databaseInstalled = true;
        clickToStartView.setText(myUI.PRESS_TO_START);
        clickToStartView.startAnimation(myBlinkAnim);
    }
}

Это важные части класса WordDBAdapter,который также используется основным действием:

public class WordDBAdapter {
    private DatabaseHelper mDbHelper;
    private SQLiteDatabase mDb;

    public WordDBAdapter open() throws android.database.SQLException {
        mDbHelper = new DatabaseHelper(mCtx);
        mDb = mDbHelper.getWritableDatabase();
        return this;
    }

    public void close() {
        mDbHelper.close();
    }
    ...
}

Я получаю следующие исключения, которые похожи, но имеют другое сообщение:

Первый тип сообщения об ошибке:

java.lang.RuntimeException: Unable to start activity 
  ComponentInfo{example.flashcards.medical/com.example.flashcards.common.SplashWindow}: 
  android.database.sqlite.SQLiteException: database is locked: BEGIN EXCLUSIVE;
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1830)
....
Caused by: android.database.sqlite.SQLiteException: database is locked: BEGIN EXCLUSIVE;
at android.database.sqlite.SQLiteDatabase.native_execSQL(Native Method)
at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1870)
at android.database.sqlite.SQLiteDatabase.beginTransactionWithListener(SQLiteDatabase.java:602)

Второй тип сообщения об ошибке:

java.lang.RuntimeException: Unable to start activity 
  ComponentInfo{example.flashcards.medical/com.example.flashcards.common.SplashWindow}: 
  android.database.sqlite.SQLiteException: database is locked
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1768)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1784)
....
Caused by: android.database.sqlite.SQLiteException: database is locked
at android.database.sqlite.SQLiteDatabase.native_setLocale(Native Method)
at android.database.sqlite.SQLiteDatabase.setLocale(SQLiteDatabase.java:1987)
at android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1855)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:820)
at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:854)

Мне бы очень не хотелось создавать ContentProvider, потому что я считаю его излишним, если доступно более простое решение.Кроме того, только это приложение имеет доступ к базе данных.

Есть предложения, как это можно исправить?

Ответы [ 7 ]

16 голосов
/ 29 февраля 2012

После некоторых усилий я это сделал (мое приложение работало на идеальном до android 2.3, но получило ошибку блокировки дб, когда я использовал его на планшете HoneyComb). Я использовал Семафоры (используя блокировку в критических секциях).

Пример 1

public class DbExp extends SQLiteOpenHelper{
    public static String Lock = "dblock";
    private static final String DATABASE_NAME = "db.db";
    private static final String TABLE_NAME = "table_name";

    public void delete(Context mContext){
        synchronized(Lock) {
            SQLiteDatabase db  = getWritableDatabase();
            db.delete(TABLE_NAME, null, null);
            db.close();
        }
    }

    public void insert(){
        synchronized(Lock) {
            SQLiteDatabase db  = getWritableDatabase();
            db.insert(TABLE_NAME, ..., ...);
            db.close();
        }
    }

}

Пример 2

public class DB {
    public static String Lock = "dblock";
    private static final String DATABASE_NAME = "db.db";
    private static final String TABLE_NAME = "table_name";

    public void delete(Context mContext){
        synchronized(Lock) {
            SQLiteDatabase db  = mContext.openOrCreateDatabase(DATABASE_NAME, Context.MODE_PRIVATE, null);
            db.delete(TABLE_NAME, null, null);
            db.close();
        }
    }

    public void insert(Context mContext){
        synchronized(Lock) {
            SQLiteDatabase db  = mContext.openOrCreateDatabase(DATABASE_NAME, Context.MODE_PRIVATE, null);
            db.insert(TABLE_NAME, ..., ...);
            db.close();
        }
    }


}

Надеюсь, это поможет любому в будущем:)

5 голосов
/ 01 апреля 2013

Если вы используете фрагменты, и это происходит с изменением ориентации, это потому, что у вас есть два экземпляра одного и того же фрагмента, и у них обоих есть свой собственный экземпляр класса помощника Db некоторого вида. Чтобы заставить его работать, вы должны либо уничтожить свой фрагмент при изменении ориентации и перестроить его, либо найти способ уничтожить вспомогательный класс Db. Это только для фрагментов с проблемами ориентации. Я должен был сделать много устранения неполадок, чтобы выяснить это, так что, надеюсь, кто-то найдет это полезным

4 голосов
/ 19 февраля 2015

Это была причина для меня:

Я забыл завершить транзакцию в своем вызове функции после вставки в БД. Поэтому, когда я пытался вставить другую запись из другой операции, я получил исключение блокировки базы данных. Добавление следующего утверждения решило мою проблему.

sqlDB.endTransaction();
1 голос
/ 03 апреля 2013

Похоже, это старый вопрос, но, возможно, это кому-нибудь поможет.У меня была та же проблема, я загружал, вероятно, около дюжины таблиц в одну базу данных и регулярно зависал, не всегда, но иногда.

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

Как я уже сказал, вы можете считать это хакейским, но это исправило мою проблему.*

1 голос
/ 05 октября 2011

Я бы предложил вам закрыть вашу БД в главном потоке, а затем открыть и закрыть ее в вашем методе doInBackground:

    @Override
    protected Integer doInBackground(String... arg0)
    {
        db.open();
        try {
        // your code here
        } finally {
            db.close()
        }            
        return 1;
    }

Также не следует напрямую вызывать методы пользовательского интерфейса из AsyncTask, поскольку пользовательский интерфейс не является потокобезопасным. Вместо этого вызовите метод updateProgress из doInBackground и обновите ваш progressDialog из onProgressUpdate, так как onProgressUpdate вызывается из основного потока.

0 голосов
/ 18 июля 2015

Эти строки кода решили мою (ту же) проблему:

(моя транзакция была открыта раньше ...)

writableDatabase.endTransaction();
writableDatabase.close();
dbHelper.close();
0 голосов
/ 05 октября 2011

Возможно, это связано с тем, что вы неправильно открыли или создали базу данных. попробуйте, если это так.

mDb = mCtx.openOrCreateDatabase(DatabaseName, SQLiteDatabase.CREATE_IF_NECESSARY, null);

в классе DatabaseHelper:

public DatabaseHelper(Context aContext)
{       
    mDb = aContext.openOrCreateDatabase(DatabaseName, SQLiteDatabase.CREATE_IF_NECESSARY, null);    
    OpenHelper openHelper = new OpenHelper(aContext, mDb );     
    mDb = openHelper.getWritableDatabase();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...