Realm작업을 하다가 보게되는 에러로그중 가장 흔한 것이 바로

"Realm access from incorrect thread"입니다.

역시 Thread에 관한 것 인데요.

Realm은 다른 DB보다 더 Thread와 관련해서는 주의를 기울여 주어야 합니다.

 

어떤 사람들은 Realm 에서 쓰레드 문제가 그리 어렵지 않다고도 하는데요.

일정한 Realm만의 규칙을 알고 있다면 그런것 같습니다.

오늘은 이것에 대해서 정리해 보도록 하겠습니다.

 

1. Realm과 Thread

 

1-1. Thread간 Realm객체 교환 금지

Realm, RealmObject 그리고 RealmResults 이 세가지 객체를 중요하게 봐야하는데요.

이들은 절대 다른 쓰레드로 넘겨서는 않됩니다.

특히나 Realm객체를 생성하는 getIntance함수는 인자로 context를 필요로 하기 때문에,

MVVM같은 디자인 패턴을 쓰면서 realm 객체를 인자로 넘겨서 사용하는 경우도 있는데요.

이 때, 쓰레드를 보지 않고 무심코 다른 쓰레드로 넘겨서 사용하면 결국 에러가 발생합니다.

 

예를 들면, onCreate에서 Realm.getInstance함수로 객체를 가져와서,

mRealm이라는 변수에 담아서 사용했다고 가정해 보겠습니다.

이 때, 다른 작업을 하다가 비동기 쓰레드에서,

해당 mRealm이라는 변수를 사용했을 경우,

문제가 발생하게 된다는 것이지요.

위에서 말했듯이, Realm, RealmObject 그리고 RealmResults는 절대로

쓰레드간 이동을 해서는 않되기 때문입니다.

 

하지만 작업을 하다보면,

Activity에서 글로벌 변수로 사용하는 mRealm과 같은 변수는

무슨 쓰레드에서 생성했었는지 까먹게 되기도 합니다.

 

그럼 다른쓰레드에서 db를 사용하려면 어떻게 해야 하나요?

Realm.getInstance와 같은 함수를 사용해서 객체를 새롭게 해당 쓰레드에서 얻어와서 사용해야 합니다.

 

 

 

1-2. findAllAsync()와 addChangeListener

findAllAsync를 메인쓰레드에서 사용하면,

realm이 알아서 워커쓰레드에서 비동기로 데이터를 조회하고 쓰게 됩니다.

조회된 데이터는 메인쓰레드에 있는 리스너로 보내주구요.

그런데, 메인쓰레드가 아닌 다른 워커쓰레드에서 findAllAsync를 호출하는 것도 문제가 될 수 있고,

리스너를 워커쓰레드에서 호출할 경우도 문제가 될 수 있습니다.

 

또한 사용한 리스너는 onStop혹은 onDestroy에서 항상 닫아주어야 합니다.

변수로 저장한 리스너에 대해서 혹은 전체 리스너에 대해서 닫을 수 있도록,

realm은 함수를 제공해 주공 있습니다.

removeChangeListener(리스너) 혹은 removeAllChangeListeners라는 함수를 제공해 주고 있습니다.

 

2. Object is no longer valid to operate on. Was it deleted by another thread?

 

Realm을 사용하다보면 만나게 될 수 있는 IllegalStateException이 있는데요.

아래와 같은 문구를 보게 됩니다.

 

 

문구를 보면, RealmObject가 사라졌다는 것인데요.

다른 쓰레드에 의해서 지워졌다는 것 이지요.

해결방법은 두가지가 될 것입니다.

첫번째는, 객체를 delete한 부분의 코드에서 Thread문제는 없었는지 확인하는 것 이구요.

두번째는, 객체를 읽을 때, executeTransaction을 통해서

 

 

 

 

 

 

 

 

2. 해결책

해결책은 수정하고자 하는 쓰레드로 Realm객체를 넘겨서 사용하는 것이 아니라,

해당 쓰레드에서 Query를 해서 사용하면 됩니다.

여기서 말하는 Realm객체에는 RealmResults들도 포함되어 집니다.

 

 

 

3. Was it deleted by another thread?

Realm을 사용하면서 종종 보게되는 에러로그 중에 하나인데요.

다른 쓰레드에서 삭제된 Realm에 접근하면서 생기는 에러입니다.

전체 문장은 아래와 같은데요.

 

Object is no longer valid to operate on. Was it deleted by another thread?

쉽게 생각하면, 다른 쓰레드에서 조작이 된 Realm객체가 반영되지 않았다는 것 인데요.

 

 

 

 

 

구체적인 해결책을 보기전에,

RealmResults와 FindAllAsync에 대해서 알아보도록 하겠습니다.

 

4. RealmResults

 

RealmResults코드를 보면 다음과 같이,

이 객체들은 Realm객체에서 list로 카피된 것이 아니고, RealmResult로 부터 Referencing된 것이라는 부분이 나옵니다.

RealmResults는 무언가를 가지고 있는 객체가 아니라, 레퍼런싱만 하고 있는 것 이지요.

 

4-1. RealmResults에 대해 알고 있어야 할 부분들

또한 RealmResults는 쓰레드간에 주고 받을 수 있는 객체가 아닙니다.

 

RealmResults가 메인쓰레드에서 존재한다면, 자동으로 업데이트 되지만,

다른 쓰레드에 있을 경우는 results를 매번 업데이트 해 주어야 하는 부분도 알고 있어야 되겠네요.

 

마지막으로는 RealmResults는 null체크보다는 사이즈 체크를 하라고 하고 있습니다.

이러한 제약 사항들을 잘 생각해서 코딩을 해야 합니다.

 

5. findAllAsync

findAllAsync는 query에 매칭되는 결과값을 백그라운드 쓰레드에서 찾습니다.

그리고 이 메소드는 메인쓰레드에서만 사용가능합니다.

 

그리고 UI쓰레드에 있어야하는 리스너에게 전달해 주지요.

 

 

6. RealmChangeListener

interface인 RealmChangeListener는

 

 

6. 문제가 발생할 수 있는 부분들

사용하기 쉬운 API를 가지고 있지만, 쓰레드 문제에 있어서만큼은

매우 섬세하게 접근해야만 하는 RealmDB입니다.

 

6-1. Realm객체나 RealmResults를

 

 

 

 

728x90

+ Recent posts