Realm Blog

Realm 자바 0.84 — 비동기 질의와 트랜잭션!

새 버전의 Realm 자바를 선보입니다. 웹 사이트와 Maven에서 받을 수 있습니다. 이 릴리즈는 비동기 질의와 워커 스레드에 대한 비동기 쓰기 트랜잭션을 포함합니다!

안드로이드 어플리케이션은 본질적으로 비동기적입니다. 이런 이유 때문에 Realm 읽기 질의와 워커 스레드에서 비동기로 쓰기 연산를 지원해달라는 요청이 가장 많았습니다.

비동기 질의

이제 워커 스레드에서 비동기로 도는 질의를 할 수 있습니다.

비동기 질의는 기존의 동기 질의와 비슷합니다. 질의를 하던 익숙한 API를 그대로 사용할 수 있습니다. 호출 단계의 마지막 메서드가 RealmQuery가 동기로 동작할지 (findAll()) 비동기로 동작할지 (findAllAsync())를 결정하죠.

예제: “John”과 “Peter” 사용자들 찾아봅시다.

처음으로 질의를 만듭시다.

RealmResults<User> result = realm.where(User.class)
                              .equalTo("name", "John")
                              .or()
                              .equalTo("name", "Peter")
                              .findAllAsync();

질의가 블록되지 않고 즉시 RealmResults<User>를 반환하는 것을 주목하세요. 이것은 (표준 자바의 Future와 비슷한) 프로미스(promise) 입니다. 질의는 백그라운드 스레드에서 계속 실행이 되고 한번 완료되면 적합한 결과로 반환한 RealmResults 인스턴스를 갱신합니다.

질의가 끝났다고 알림을 받기 위해 콜백을 등록할 수 있습니다. 콜백은 Realm의 마지막 값을 반영하도록 질의가 갱신될 때마다 호출이 됩니다. (일반적으로 커밋 후에요.)

private RealmChangeListener callback = new RealmChangeListener() {
    @Override
    public void onChange() { // called once the query complete and on every update
    // use the result
    }
};

public void onStart() {
    RealmResults<User> result = realm.where(User.class).findAllAsync();
     result.addChangeListener(callback);
}

리스너에서 강한 참조를 가지고 있습니다. 종료된 객체의 누수를 막기 위해 등록된 레지스터를 잊지 말고 해제하세요.

public void onStop () {
    result.removeChangeListener(callback); // remove a particular listener
    // or
    result.removeChangeListeners(); // remove all registered listeners
}

언제든지 RealmResults가 끝났는지 아닌지 확인할 수 있습니다.

result.isLoaded()

동기적으로 얻은 RealmResults의 isLoaded 를 호출하면 항상 참을 받게 됩니다.

루퍼를 사용하지 않는 스레드 경고: 비동기 질의는 Realm이 끊임없이 결과를 전달하기 위해 핸들러가 필요합니다. 루퍼 없이 스레드 안에서 비동기 질의를 호출하면 IllegalStateException 예외가 발생합니다.

비동기 트랜젝션

이제 워커 스레드에 비동기로 쓰기 트렌젝션을 수행할 수 있습니다.

현재 executeTransaction를 호출하는 방법과 동일합니다. Realm 스레드를 열었던 스레드 대신에 일을 할 다른 스레드에서 Realm이 열립니다.

트랜잭션이 끝나거나 실패할 때 알림을 받고 싶다면 콜백을 등록할 수 있습니다. (이는 Realm의 핸들러가 콜백을 전달하길 요구합니다. 만약 루퍼가 없는 스레드에서 Realm을 열고 비동기 쓰기를 시작하면 알림을 밪지 못합니다.)

realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm bgRealm) {
                User user = bgRealm.createObject(User.class);
                user.setName("John");
                user.setEmail("[email protected]");
            }
        }, new Realm.Transaction.Callback() {
            @Override
            public void onSuccess() {
            }

            @Override
            public void onError(Exception e) {
                // transaction is automatically rolled-back, do any cleanup here
            }
        });

콜백은 선택적입니다. 호출하는 걸로 끝내고 싶다면 그렇게 해도 됩니다.

realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm bgRealm) {
                User user = bgRealm.createObject(User.class);
                user.setName("John");
                user.setEmail("[email protected]");
            }
        }, null);

쓰기 트랜잭션이 끝날 때까지 강한 참조를 가지고 있습니다. (예를들어 액티비티나 프래그먼트를 종료해서) 만일 트랜잭션을 취소하길 원한다면 cancel을 호출하면 됩니다. 이는 인스턴스나 둘러싼 객체의 누수를 막기 위해서 스케쥴된 트랜잭션을 취소하고 콜백에 대한 참조를 제거합니다.

RealmAsyncTask transaction = realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm bgRealm) {
            }
        }, null);

// configuration change ...

public void onStop () {
    if (transaction != null && !transaction.isCancelled()) {
        transaction.cancel();
    }
}

새로운 질의 옵션

우리는 새로운 방식의 질의 두가지 isEmpty()distinct()를 추가하였습니다.

RealmQuery.isEmpty()를 이용하면 값이 널이 아니고 비어있는 것을 확인할 수 있습니다. 예를 들어 RealmList나 바이트 배열이 0개의 요소를 가지고 있다면 비어있는 것이고 문자열이 ""이면 비어있는 것입니다. isEmpty()는 정수 값에서는 동작하지 않습니다.

// Find all users with no dogs
RealmResults<User> users = realm.where(User.class).isEmpty("dogs").findAll();

// Find all users with at least 1 dog
RealmResults<User> users = realm.where(User.class).not().isEmpty("dogs").findAll();

Realm.distinct()는 주어진 값으로 정의된 요소에 대한 대조적인 세트를 얻기 위해 쓰입니다. distinct가 적용이 되는 필드는 인덱스(@Index 또는 @PrimaryKey)여야 합니다.

// Returns the set of users that all have a different name
RealmResults<User> users = realm.distinct(User.class, "name");

다른 유용한 메서드들

상호작용을 하기 전에 상태를 질의하는게 가능하도록 여러 응용 메서드를 추가하였습니다.

  • Realm.isClosed(): 하부의 Realm 파일이 여전히 열려있는지 확인합니다.
  • Realm.isInTransaction(): Realm파일이 여전히 쓰기 트랜잭션 상태인지 확인합니다.
  • RealmQuery.isValid(), RealmList.isValid(), RealmResults.isValid(): 하부의 Realm이 닫혀있거나 데이터가 삭제되었는지 확인합니다.

더 구체적인 내용이 궁금하면 변경점을 확인해보세요.


읽어주셔서 감사합니다. 이제 Realm과 함께 앞으로 나아가 멋진 앱을 만들어 봅시다! 우리는 항상 StackOverflow, GitHub, and Twitter에 있습니다. 페북에서 운영중인 Realm 한국 사용자 그룹 이나 Realm 한국 사용자 페이지와도 방문해 주십시오. 한국어 연락처는 [email protected] 입니다.


Realm Team

Realm의 미션은 더 나은 앱을 빠르게 개발할 수 있도록 돕는 것입니다. 이를 위해 저희는 개발자들이 실시간 협업, 가상 현실, 라이브 데이터 동기화, 오프라인 경험, 메시징 등 정교하고 강력한 기능을 쉽게 개발할 수 있도록 하는 개발 도구와 플랫폼을 제공하고 있습니다.

저희는 모바일 인터넷이 수많은 사용자와 보다 많은 디바이스가 속한 개방형 네트워크와 이들 간의 실시간 상호 작용으로 진화할 것이라고 믿으며, 개발자가 이같은 방향으로 발전할 수 있도록 돕기 위해 저희 제품들을 개발하고 있습니다.

이런 개발 뉴스를 더 만나보세요