Android

[안드로이드 Failed to open database 오류]SQLiteDatabaseLockedException: database is loc

네트워크 관련 처리 또는 파일 다운로드 처리 등의 작업을 처리할 때 10초 이상의 작업시간이 걸릴 경우, ANR에 빠질 수 있습니다. 이러한 작업을 처리할 때는 백그리운드 서비스에서 처리를 하거나 쓰레드를 사용하여 작업을 해야 합니다. AsyncTask를 사용하여 처리하게되면 진행상태를 표기할 수 도 있습니다.
앱을 사용자가 최초로 다운로드 받을 때 데이터베이스 쓰기 작업을 최초 1회 진행합니다. 보통 10초 이내로 종료되는 작업 입니다. 그러나 문제가 하나 있었습니다. 프로그레스 다이얼로그를 사용하지 않고, intro 액티비티창 자체에 프로그레스 바 즉 로딩바를 추가했습니다. 그랬더니, 데이터베이스 쓰기 작업을 하는 동안에 로딩바가 멈추는 현상이 발생하였고, 이것을 처리해보고자 쓰레드 작업을 진행하였는데, 여전히 로딩바는 멈추는 현상은 그대로 였고, 새로운 오류가 발생하였습니다.
쓰기 작업완료 후 메인화면으로 진입했을 때 데이터베이스를 읽어오는데 이 작업을 할 수 없는 상태가 발생하였으며, 쓰기 작업이 아직 끝나지 않은 상태인 것인지, 데이터베이스에 락이 걸렸습니다. 다중 쓰레드 작업이 문제를 일으킨 것으로 보였습니다.

2021-05-28 09:39:51.629 15752-15752/com.test E/SQLiteDatabase: Failed to open database '/data/user/0/com.test/databases/test_my_database.db'. android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5 SQLITE_BUSY) at android.database.sqlite.SQLiteConnection.nativeExecute(Native Method) at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:707) at android.database.sqlite.SQLiteConnection.setLocaleFromConfiguration(SQLiteConnection.java:460) at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:261) at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:205) at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:505) at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:206) at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:198) at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:918) at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:898) at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:762) at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:751) at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:373) at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316) at com.test.ui.home.HomeFragment.getLastWinnerNumberRound(HomeFragment.java:548) at com.test.ui.home.HomeFragment.onCreateView(HomeFragment.java:348) at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698) at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356) at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1434) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1497) at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:447) at androidx.fragment.app.FragmentManager.executeOps(FragmentManager.java:2169) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1992) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1947) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849) at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2629) at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2577) at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:247) at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:541) at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:210) at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1435) at android.app.Activity.performStart(Activity.java:8024) at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3475) at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221) at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:223) at android.app.ActivityThread.main(ActivityThread.java:7656) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 2021-05-28 09:39:51.632 15752-15752/com.test E/AndroidRuntime: FATAL EXCEPTION: main Process: com.test, PID: 15752 android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5 SQLITE_BUSY) at android.database.sqlite.SQLiteConnection.nativeExecute(Native Method) at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:707) at android.database.sqlite.SQLiteConnection.setLocaleFromConfiguration(SQLiteConnection.java:460) at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:261) at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:205) at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:505) at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:206) at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:198) at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:918) at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:898) at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:762) at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:751) at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:373) at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316) at com.test.ui.home.HomeFragment.getLastWinnerNumberRound(HomeFragment.java:548) at com.test.ui.home.HomeFragment.onCreateView(HomeFragment.java:348) at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698) at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356) at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1434) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1497) at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:447) at androidx.fragment.app.FragmentManager.executeOps(FragmentManager.java:2169) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1992) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1947) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849) at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2629) at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2577) at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:247) at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:541) at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:210) at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1435) at android.app.Activity.performStart(Activity.java:8024) at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3475) at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221) at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:223) at android.app.ActivityThread.main(ActivityThread.java:7656) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 2021-05-28 09:39:52.020 15752-15752/com.test E/UncaughtException: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5 SQLITE_BUSY) at android.database.sqlite.SQLiteConnection.nativeExecute(Native Method) at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:707) at android.database.sqlite.SQLiteConnection.setLocaleFromConfiguration(SQLiteConnection.java:460) at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:261) at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:205) at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:505) at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:206) at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:198) at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:918) at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:898) at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:762) at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:751) at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:373) at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316) at com.test.ui.home.HomeFragment.getLastWinnerNumberRound(HomeFragment.java:548) at com.test.ui.home.HomeFragment.onCreateView(HomeFragment.java:348) at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698) at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356) at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1434) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1497) at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:447) at androidx.fragment.app.FragmentManager.executeOps(FragmentManager.java:2169) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1992) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1947) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849) at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2629) at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2577) at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:247) at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:541) at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:210) at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1435) at android.app.Activity.performStart(Activity.java:8024) at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3475) at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221) at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:223) at android.app.ActivityThread.main(ActivityThread.java:7656) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)

메인화면 진입 후 데이터베이스 호출해서 처리하는 작업에 해들러 작업을 통하여 3초 정도 딜레이를 주면 문제는 해결되었습니다. 그러나 이 방법은 꼼수라…. 다중 스레드를 사용하여 접근하였음으로 발생한 오류였기에 메인화면, 즉 두번째 작업이 오래 걸리는 작업이 아니라면, 메인화면의 쓰레드 작업을 제거 후 데이터베이스 읽기 또는 쓰기 작업을 하면 해결되었습니다. 다른 방법이 있을까해서 구글링한 내용을 아래에 첨부합니다.

또 다른 해결방법은 enableWriteAheadLogging()를 사용하여 상태를 체크하는 방법이다.

public boolean enableWriteAheadLogging() { 
	return database.enableWriteAheadLogging(); 
}
/ ** * 데이터베이스에 대한 미리 쓰기 로깅 사용을 활성화 또는 비활성화합니다. 
* * 미리 쓰기 로깅은 읽기 전용 데이터베이스에서 사용할 수 없으므로 
* 데이터베이스가 읽기 전용으로 열린 경우이 플래그는 무시됩니다. 
* * @param enabled 미리 쓰기 로깅을 활성화해야하는 경우 True, 활성화 된 경우 False 
* 비활성화되어야합니다. 
* * @ SQLiteDatabase # enableWriteAheadLogging () 참조 
* / 
public void setWriteAheadLoggingEnabled(boolean enabled) 
{ 
    synchronized (this) { 
      if (mEnableWriteAheadLogging != enabled) { 
        if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) { 
            if (enabled) { mDatabase.enableWriteAheadLogging(); 
            } else { 
                 mDatabase.disableWriteAheadLogging();
            } 
        } 
          mEnableWriteAheadLogging = enabled; 
      } 
	} 
} 


데이터베이스가 잠긴 이유

(1) 다중 스레드 액세스로 인한 데이터베이스 잠금

SQLite는 동시에 하나의 쓰기 작업 만 수행 할 수 있습니다. 동시에 두 개의 쓰기 작업이있을 경우 후속 실행은 먼저 대기 할 수 있으며 대기 시간이 5 초를 초과하면이 오류가 발생합니다. 동일한 파일이 기록되고 있으며 데이터베이스 열기 작업을 반복하면이 문제가 발생할 가능성이 더 큽니다.
스레드 A가 현재 데이터베이스에 액세스하는 경우 스레드 B도 이때 데이터베이스에 액세스해야합니다. 이렇게하면 스레드 B에서 위와 유사한 예외가 발생합니다. 비동기식을 방지하기 위해 데이터베이스 액세스 방법을 동기식으로 설정해야합니다. 호출 방법에 동기화 된 수정자를 추가하는 것과 같은 문제가 있습니다.
동기화 된 키워드를 사용하여 데이터베이스 연결을 얻는 방법을 수정하거나 isDbLockedByOtherThreads 메서드를 사용하여 데이터베이스가 잠겨 있는지 확인한 다음 액세스하기 전에 일정 시간 동안 기다립니다.

(2) 트랜잭션 작업의 실행이 정상적으로 종료되지 않은 경우.

DB에 쓰기 작업을 할 때 트랜잭션을 사용하여 데이터베이스를 운영하지만 쓰기작업을 통한 트랜잭션이 실행 된 후 db.endTransaction();를 호출하지 않아 트랜잭션을 닫지 않았을 때 발생합니다.

(3) SQLite 자체 문제

때때로 프로그램을 디버깅 할 때 위의 예외가 로그 콘솔에서 자주 발생하는 것을 발견하지만 프로그램 실행시에는 이러한 문제가 없습니다. 이 현상은 ResultSet을 디버깅 할 때도 발생합니다. 정보를 검색하는 이유는 SQLite 데이터를 사용할 수 없습니다. 완전히 스레드로부터 안전하므로 일부 위치에서 설명 할 수 없는 문제가 발생 합니다 . 이러한 상황이 발생하면 데이터베이스가 연결된 위치로만 중단 점에 도달 할 수 없습니다.

좀 더 많은 정보가 필요하다면 아래 글을 참고하자.
https://www.py4u.net/discuss/659173

https://blog.csdn.net/u011386173/article/details/88558099

https://android.binarydoc.org/com.android/sdk/24/method?classfilelocation=android.database.sqlite.sqlitedatabase%28android-24%2Fandroid.jar%2Fandroid%2Fdatabase%2Fsqlite%2Fsqlitedatabase.class%29&seq=64&methodname=enableWriteAheadLogging

https://vimsky.com/examples/detail/java-method-android.database.sqlite.SQLiteDatabase.enableWriteAheadLogging.html

개발자문서 : SQLiteDatabaseLockedException

Leave a Reply

error: Content is protected !!