관리 메뉴

나만을 위한 블로그

[Android] SQLite 사용 시 attempt to re-open an already-closed object 에러 해결 본문

Android

[Android] SQLite 사용 시 attempt to re-open an already-closed object 에러 해결

참깨빵위에참깨빵_ 2021. 3. 28. 17:53
728x90
반응형

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

아래는 싱글톤으로 구현하는 예시를 보여주는 스택오버플로우 게시글이다.

stackoverflow.com/questions/31965562/java-lang-illegalstateexception-attempt-to-re-open-an-already-closed

 

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

 

반응형
Comments