[Android] SQLite 사용 시 attempt to re-open an already-closed object 에러 해결
SQLite를 사용해 서버에서 받아온 토큰값을 저장한 다음 다른 프래그먼트에서 불러오던 도중 이런 에러를 만났다.
에러 원인은 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
Sqlite - 데이터베이스 동시 접근
데이터베이스 동시 접근 – Sqlite1 SQLiteOpenHelper 구현이 되어있다고 다음과 같이 가정해보자. p...
blog.naver.com
stackoverflow.com/questions/19034391/attempt-to-re-open-an-already-closed-object-sqlitedatabase
attempt to re-open an already-closed object: SQLiteDatabase:
I'm trying to delete something from a database then insert a new value. I don't know much about databases, so would appreciate advice on whats going wrong here. I keep getting the following error:
stackoverflow.com
아래는 싱글톤으로 구현하는 예시를 보여주는 스택오버플로우 게시글이다.
Java.lang.IllegalStateException: attempt to re-open an already-closed?
I got error from my Sqlite Android. I have code like this: public int UpdatePhotoUser(String foto_user, int metode) { SQLiteDatabase db = this.getWritableDatabase(); ContentValues
stackoverflow.com