일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 자바 다형성
- 플러터 설치 2022
- 스택 큐 차이
- 멤버변수
- jvm이란
- 큐 자바 코드
- 안드로이드 유닛테스트란
- 안드로이드 라이선스
- android retrofit login
- 2022 플러터 안드로이드 스튜디오
- ANR이란
- 안드로이드 라이선스 종류
- 클래스
- rxjava hot observable
- 안드로이드 레트로핏 사용법
- 서비스 vs 쓰레드
- 스택 자바 코드
- Rxjava Observable
- 안드로이드 유닛 테스트 예시
- jvm 작동 원리
- rxjava cold observable
- 안드로이드 유닛 테스트
- 2022 플러터 설치
- 서비스 쓰레드 차이
- 안드로이드 레트로핏 crud
- 객체
- ar vr 차이
- android ar 개발
- rxjava disposable
- 안드로이드 os 구조
- Today
- Total
나만을 위한 블로그
[Android] SQLite 사용 시 attempt to re-open an already-closed object 에러 해결 본문
[Android] SQLite 사용 시 attempt to re-open an already-closed object 에러 해결
참깨빵위에참깨빵_ 2021. 3. 28. 17:53SQLite를 사용해 서버에서 받아온 토큰값을 저장한 다음 다른 프래그먼트에서 불러오던 도중 이런 에러를 만났다.
에러 원인은 INSERT를 수행하는 메서드였는데, 이 메서드의 처음 형태는 이런 모양이었다.
ContentValues values = new ContentValues();
values.put(CreateDB.TOKEN, token);
return mDB.insert(CreateDB._TABLENAME, null, values);
이렇게 쓰고 액티비티에선 이렇게 처리했었다.
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
helper = new DBOpenHelper(this);
helper.open();
helper.create();
.
.
.
helper.insertColumn(token);
.
.
.
@Override
protected void onPause()
{
super.onPause();
helper.close();
}
onCreate(), onPause()에서 각각 open()과 close()를 호출해서 열고닫는 작업을 해줬으니 다른 화면에서 SQLite를 open()해서 연 뒤 쓰더라도 문제는 없을 거라 생각한 게 문제였다.
이렇게 쓰는 것 자체가 에러를 일으키는 원인이었다.
찾아보니 내가 하나의 DB 연결을 쓰기 전 getReadableDatabase(), getWritableDatabase()는 여러 쓰레드(쓰레드1, 쓰레드2라고 가정한다)에 대해 똑같은 SQLiteDatabase 객체를 리턴해야 한다.
그런데 쓰레드2가 쓰고 있을지도 모르는(어쩌면 쓰고 있는) DB를 쓰레드1이 닫아 버리는 일이 위 코드로 인해 발생했기 때문에 앱이 에러를 뿜으며 죽은 것이다.
또한 SQLite에 매번 쿼리를 수행할 때마다 open/close()하는 건 이것도 오버헤드를 발생시킬 여지가 있다고 한다.
그럼 close()를 안 쓰면 되지 않을까 생각해서 해봤지만 앱은 죽지 않고 정상적으로 작동했다.
하지만 DB를 open한 채로 그냥 냅두려니 찜찜한 게 있어서 open() 후 저장 처리를 수행한 다음, 다 끝나면 DB가 open되어 있는지 아닌지 상태를 확인해서 close()하는 로직을 구현해보기로 했다.
그러기 위해 먼저 INSERT를 수행하는 메서드 내부 로직부터 스택오버플로우를 참고해 뜯어고쳤다.
public long insertToken(String token)
{
SQLiteDatabase sqlite = null;
try
{
sqlite = mDBHelper.getWritableDatabase();
synchronized (sqlite)
{
ContentValues values = new ContentValues();
values.put(CreateDB.TOKEN, token);
return mDB.insert(CreateDB._TABLENAME, null, values);
}
}
finally
{
if (sqlite != null && sqlite.isOpen())
{
sqlite.close();
}
}
}
synchronized()를 쓰기 위해 try문을 썼고, synchronized() 블럭 안에 INSERT를 수행하는 코드를 집어넣었다.
finally 블럭에는 SQLiteDatabase 객체의 상태를 확인해서 객체가 열린 채로 존재하고 있을 경우 close()하는 메서드를 호출했다.
이렇게 수정하니 더 이상 앱은 에러와 함께 죽는 일이 없었다. 에뮬레이터와 공기계에서도 문제없이 잘 작동했다.
이렇게 하는 방법 말고도 싱글톤을 활용하는 방법도 있다 하니 아래 글을 참고하자.
참고)
m.blog.naver.com/tommybee/220730261512
stackoverflow.com/questions/19034391/attempt-to-re-open-an-already-closed-object-sqlitedatabase
아래는 싱글톤으로 구현하는 예시를 보여주는 스택오버플로우 게시글이다.
'Android' 카테고리의 다른 글
[Android] ArrayList 안의 값이 [[a, b, c]] 형태일 때 처리 방법 (0) | 2021.05.10 |
---|---|
[Android] editText 클릭 시 키보드가 레이아웃을 밀어올리지 않게 하는 법 (0) | 2021.05.05 |
[Android] 구글 애널리틱스 적용하는 방법 (0) | 2021.02.23 |
[Android] EditText에 띄어쓰기 포함 글자수를 제한하는 방법 (0) | 2021.02.17 |
[Android] 리사이클러뷰의 리스트에 데이터가 없을 때 '데이터가 없습니다' 텍스트뷰 띄우는 방법 (0) | 2021.02.17 |