Android

[Android]SQLiteDatabase - 트랜잭션 & 비동기 처리

김한토 2025. 5. 12. 09:57
반응형

들어가며

안드로이드 앱 개발 중 가장 까다로운 문제 중 하나는 데이터베이스 작업과 비동기 처리의 조화입니다. 최근 우리 프로젝트에서 발생한 "attempt to re-open an already-closed object: SQLiteDatabase" 오류를 해결하는 과정에서 배운 교훈과 인사이트를 공유하고자 합니다. 이 글이 같은 문제로 고민하는 개발자들에게 도움이 되길 바랍니다.

문제 상황: 데이터베이스 닫힘 오류

우리 앱에서는 서버로부터 다양한 데이터(상품, 광고 이미지 등)를 가져와 SQLite 데이터베이스에 저장한 후 이미지 파일을 다운로드하는 기능이 있었습니다. 특히 문제가 발생한 코드는 광고 이미지 데이터를 처리하는 메소드였습니다.


이 코드는 간헐적으로 "attempt to re-open an already-closed object: SQLiteDatabase" 오류를 발생시켰습니다.

문제 분석: 왜 이런 오류가 발생했을까?

문제의 근원은 트랜잭션 관리와 비동기 작업의 상호작용에 있었습니다. 디버깅을 통해 다음과 같은 문제점을 발견했습니다:

  1. 메인 데이터 파서 메소드는 하나의 큰 트랜잭션 내에서 여러 파서를 순차적으로 호출했습니다.
  2. 광고 이미지 파서는 ExecutorService와 HandlerThread를 사용한 비동기 방식으로 이미지 다운로드 및 DB 작업을 수행했습니다.
  3. 문제는 메인 트랜잭션이 종료된 후에도 비동기 작업들이 계속 실행되면서 이미 닫힌 데이터베이스 객체에 접근하려고 시도한다는 점이었습니다.

해결 방법: 일관된 비동기 패턴 적용

해결 방법은 이미 앱 내에서 잘 작동하고 있던 상품 이미지 처리 패턴(prParser())을 참고하여 일관된 접근 방식을 적용하는 것이었습니다.

구현한 방법은 다음과 같습니다:

  1. 트랜잭션과 비동기 작업의 분리 광고 이미지 파서를 수정하여 트랜잭션 내에서는 기본 정보만 저장하고, 이미지 다운로드 정보는 별도의 컬렉션(HashMap)에 수집하도록 변경했습니다.
  2. AsyncTask를 활용한 이미지 일괄 처리 기존에 상품 이미지 처리에 사용하던 ImageDownloadTask(AsyncTask 상속)를 확장하여 광고 이미지도 함께 처리하도록 수정했습니다. 이 작업은 메인 트랜잭션이 완료된 이후에 실행됩니다.
  3. 별도 트랜잭션을 이용한 안전한 DB 업데이트 이미지 다운로드 후 데이터베이스 경로 업데이트 작업은 AsyncTask의 onPostExecute() 메소드에서 별도의 트랜잭션으로 안전하게 처리하도록 구현했습니다.

 

+

 

AsyncTask란 무엇인가?

AsyncTask는 안드로이드 플랫폼에서 제공하는 클래스로, 백그라운드 작업을 실행하고 그 결과를 UI 스레드에 쉽게 게시할 수 있도록 설계되었습니다. 주요 메소드로는:

  1. doInBackground(): 백그라운드 스레드에서 실행되는 주요 작업
  2. onPreExecute(): 백그라운드 작업 전 UI 스레드에서 실행
  3. onProgressUpdate(): 진행 상황을 UI 스레드에 업데이트
  4. onPostExecute(): 백그라운드 작업 완료 후 UI 스레드에서 결과 처리

 

AsyncTask가 더 이상 권장되지 않는 이유

AsyncTask는 API 30(Android 11)에서 공식적으로 deprecated 되었으며, 다음과 같은 여러 문제점을 가지고 있습니다:

  1. 생명주기 문제:
    • AsyncTask는 Activity/Fragment 생명주기와 자동으로 연결되지 않습니다.
    • 화면 회전이나 앱 백그라운드 전환 시 AsyncTask가 계속 실행되어 메모리 누수나 충돌을 유발할 수 있습니다.
  2. 스레드 풀 관리 문제:
    • API 레벨에 따라 실행 방식이 달라져 예측하기 어렵습니다.
    • 기본 스레드 풀 크기가 제한적이어서 많은 작업이 대기해야 할 수 있습니다.
  3. 설정 변경 후 결과 처리:
    • 화면 회전 같은 설정 변경 후 UI 참조가 무효화되어 결과 처리가 어렵습니다.
  4. 에러 처리 메커니즘 부재:
    • 예외 처리를 명시적으로 구현해야 하며, 기본 메커니즘이 없습니다.
  5. 취소 처리의 복잡성:
    • 작업 취소가 복잡하고 즉시 중단을 보장하지 않습니다.

 

 

https://hantoluvcoding.tistory.com/37

 

동기 / 비동기

동기(synchronous)동기 작업은 연산이 완료될 때까지 해당 작업을 호출한 스레드가 기다리는 방식이다. 즉, 작업이 완료될 때까지 다음 작업이 시작되지 않는다. 안드로이드에서는 주로 UI 스레드(Ma

hantoluvcoding.tistory.com

 

https://hantoluvcoding.tistory.com/82

 

[Android] Thread, Runnable, Executors

https://mangkyu.tistory.com/258 [Java] Thread와 Runnable에 대한 이해 및 사용법이번에는 자바 초기부터 멀티 쓰레드 기반의 동시성 프로그래밍을 위해 만들어졌던 Thread와 Runnable를 살펴보도록 하겠습니다. 1

hantoluvcoding.tistory.com

 

반응형