key-value 쌍 db
https://realm.io/docs/java/latest/#installation
Realm Java lets you efficiently write your app’s model layer in a safe, persistent, and fast way. Here’s what it looks like:
// Define your model class by extending RealmObject
public class Dog extends RealmObject {
private String name;
private int age;
// ... Generated getters and setters ...
}
public class Person extends RealmObject {
@PrimaryKey
private long id;
private String name;
private RealmList<Dog> dogs; // Declare one-to-many relationships
// ... Generated getters and setters ...
}
// Use them like regular java objects
Dog dog = new Dog();
dog.setName("Rex");
dog.setAge(1);
// Initialize Realm (just once per application)
Realm.init(context);
// Get a Realm instance for this thread
Realm realm = Realm.getDefaultInstance();
// Query Realm for all dogs younger than 2 years old
final RealmResults<Dog> puppies = realm.where(Dog.class).lessThan("age", 2).findAll();
puppies.size(); // => 0 because no dogs have been added to the Realm yet
// Persist your data in a transaction
realm.beginTransaction();
final Dog managedDog = realm.copyToRealm(dog); // Persist unmanaged objects
Person person = realm.createObject(Person.class); // Create managed objects directly
person.getDogs().add(managedDog);
realm.commitTransaction();
// Listeners will be notified when data changes
puppies.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults<Dog>>() {
@Override
public void onChange(RealmResults<Dog> results, OrderedCollectionChangeSet changeSet) {
// Query results are updated in real time with fine grained notifications.
changeSet.getInsertions(); // => [0] is added.
}
});
// Asynchronously update objects on a background thread
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm bgRealm) {
Dog dog = bgRealm.where(Dog.class).equalTo("age", 1).findFirst();
dog.setAge(3);
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
// Original queries and Realm objects are automatically updated.
puppies.size(); // => 0 because there are no more puppies younger than 2 years old
managedDog.getAge(); // => 3 the dogs age is updated
}
});
Realm이란 무엇인가?
- 모바일용 크로스 플랫폼 데이터베이스
- SQLite 기반의 ORM 프레임워크
시작하기
프로젝트에 Realm 추가
- 프로젝트의
build.gradle
파일에 다음과 같이 classpath를 추가한다.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "io.realm:realm-gradle-plugin:1.2.0"
}
}
- 앱의
build.gradle
파일에 다음과 같이 플러그인을 추가한다.
apply plugin: 'realm-android'
Realm 설정
초기화
- Realm을 사용하기 전에, 반드시 초기화해야 한다.
Realm.init()
메서드를 이용해 초기화한다. 파라미터로Context
를 받는다.
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Realm.init(this);
}
}
설정정보 변경
RealmConfiguration
객체를 이용해 Realm에 대한 설정정보를 설정한다.- 다음 예시처럼 여러가지 설정정보를 변경할 수 있다.
// The RealmConfiguration is created using the builder pattern.
// The Realm file will be located in Context.getFilesDir() with name "myrealm.realm"
RealmConfiguration config = new RealmConfiguration.Builder()
.name("myrealm.realm")
.encryptionKey(getKey())
.schemaVersion(42)
.modules(new MySchemaModule())
.migration(new MyMigration())
.build();
// Use the config
Realm realm = Realm.getInstance(config);
- 설정정보를 여러개 만들어서 각각의 데이터베이스를 구성할 수도 있다.
RealmConfiguration myConfig = new RealmConfiguration.Builder()
.name("myrealm.realm")
.schemaVersion(2)
.modules(new MyCustomSchema())
.build();
RealmConfiguration otherConfig = new RealmConfiguration.Builder()
.name("otherrealm.realm")
.schemaVersion(5)
.modules(new MyOtherSchema())
.build();
Realm myRealm = Realm.getInstance(myConfig);
Realm otherRealm = Realm.getInstance(otherConfig);
inMemotry()
메서드를 이용하면, disk에 저장되지 않고 메모리에만 임시로 있는 데이터베이스를 만들 수 있다.
RealmConfiguration myConfig = new RealmConfiguration.Builder()
.name("myrealm.realm")
.inMemory()
.build();
Realm instance Closing
- Realm은 레퍼런스 카운트 방식을 도입했기 때문에,
getInstance()
메서드를 호출해서 Realm 객체를 얻어왔으면,close()
메서드를 통해 객체를 닫아주어야 한다.- 그러나 Realm 객체는 싱글턴이므로
getInstance()
메서드를 여러번 이용해도 동일 객체가 나온다.
- 그러나 Realm 객체는 싱글턴이므로
closeable
인터페이스를 구현한 클래스이므로,JDK 7 & minSdkVersion >= 19
에서는 다음과 같이 사용할 수 있다.
try (Realm realm = Realm.getDefaultInstance()) {
// No need to close the Realm instance manually
}
Models
Models
- Realm은 기본적으로
RealmObject
를 상속받은 class들을 모델로 인식한다. RealmModel
인터페이스를 구현하고,@RealmClass
어노테이션을 붙인 일반 class도 모델로 인식한다.- RealmObject를 상속받은 객체는 Realm 프레임워크가 제공하는 인스턴스 메서드들을 그냥 사용하면 되지만, RealmModel를 구현하는 객체는 RealmObject 클래스의 static 메서드를 이용한다.
- 일반 자바 객체처럼, getter, setter 이외의 일반 메서드도 사용할 수 있다.
// RealmObject 상속
public class User extends RealmObject {
private String name;
private int age;
@Ignore
private int sessionId;
// Standard getters & setters generated by your IDE…
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public int getSessionId() { return sessionId; }
public void setSessionId(int sessionId) { this.sessionId = sessionId; }
}
//RealmModel 구현
@RealmClass
public class User implements RealmModel {
private String name;
private int age;
@Ignore
private int sessionId;
// Standard getters & setters generated by your IDE…
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public int getSessionId() { return sessionId; }
public void setSessionId(int sessionId) { this.sessionId = sessionId; }
}
// 모델 객체 사용하기
// With RealmObject
user.isValid();
user.addChangeListener(listener);
// With RealmModel
RealmObject.isValid(user);
RealmObject.addChangeListener(user, listener)
Field 타입
- boolean, byte, short, int, long, float, double, String, Date, byte[] 타입이 사용가능하다.
- 자바의 Boxed 타입인 Boolean, Byte, Short, Integer, Long, Float, Double 또한 사용할 수 있다.
- Boxed타입을 사용하면
null값 표현이 가능
하다.
- Boxed타입을 사용하면
RealmObject
,RealmList<? extends RealmObject>
타입을 이용하여 객체간 관계를 표현한다.
Required Fields & null 값
- null값이 들어갈 가능성이 있는 타입의 필드 중, null값을 허용하지 않으려면
@Required
어노테이션을 붙인다.- Boolean, Byte, Short, Integer, Long, Float, Double, String, byte[], Date 타입에
@Required
어노테이션을 붙일 수 있다. - 그 외의 타입들은
@Required
어노테이션을 붙이게 되면, 컴파일이 실패하게 된다.
- Boolean, Byte, Short, Integer, Long, Float, Double, String, byte[], Date 타입에
- Primitive 타입, RealmList 타입은 암시적으로 Required 상태이다.
- RealmObject 타입은 항상 nullable 이다.
Ignoring 속성
- DB에 저장할 필요가 없는 필드에
@Ignore
속성을 붙이면, 그 속성은 DB에 저장되지 않는다.
자동 갱신 객체
- RealmObject 객체의 필드값을 변경하면, 그 결과는 즉시 쿼리에 반영된다.(쿼리를 갱신할 필요가 없다.)
// Realm 객체 생성
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Dog myDog = realm.createObject(Dog.class);
myDog.setName("Fido");
myDog.setAge(1);
}
});
// 생성된 객체를 쿼리
Dog myDog = realm.where(Dog.class).equalTo("age", 1).findFirst();
// 객체 값 변경
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Dog myPuppy = realm.where(Dog.class).equalTo("age", 1).findFirst();
myPuppy.setAge(2);
}
});
// 재 쿼리를 하지 않아도 변경된 값 확인가능
myDog.getAge(); // => 2
속성 인덱싱
- Realm객체의 필드에
@Index
어노테이션을 추가하면, 그 필드를 기준으로 인덱싱된다. - 인덱싱을 하면, 데이터 추가가 느려지고, 파일이 커지는 단점이 있다.
- 대신, 쿼리가 빨라진다.
- Realm DB는 String, byte, short, int, long, boolean, Date 필드에만 인덱싱을 허용한다.
Primary Key
- Realm 객체에서 Primary Key를 추가하려면, 키로 이용하려는 필드에
@PrimaryKey
어노테이션을 붙인다. - Primary Key로 이용하려면
문자열(String)
타입이거나정수(byte, short, int, long / Byte, Short, Integer, Long)
이어야 한다. - 여러 필드를 함께 Primary Key로 쓸 수 없다.(Compund Key는 불가능)
@PrimaryKey
어노테이션은 암시적으로@Index
어노테이션을 포함하고 있다.@PrimaryKey
어노테이션은 필드값에 null을 허용한다. null값 허용을 막으려면@Required
어노테이션과 함꼐 사용해야 한다.- Primary Key를 지정하면,
copyToRealmOrUpdate()
메서드를 이용할 수 있따.- 업데이트 시, Key를 이용해 객체를 찾으면 그 객체를 업데이트하고, 객체를 못찾으면 새로운 객체를 생성한다.
- Primary Key 없이 메서드를 호출하면 exception이 발생한다.
Realm.createObject()
메서드로 Realm 객체를 생성하면, 모든 필드에 기본값이 채워지게 되는데, 이 경우 Key가 기본값으로 중복된 객체가 생성될 수 있다.copyToRealm()
메서드를 이용하면 이 문제를 피할 수 있다.
final MyObject obj = new MyObject();
obj.setId(42);
obj.setName("Fish");
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
// This will create a new object in Realm or throw an exception if the
// object already exists (same primary key)
// realm.copyToRealm(obj);
// This will update an existing object with the same primary key
// or create a new object if an object with no primary key = 42
realm.copyToRealmOrUpdate(obj);
}
});
커스터마이징 객체
- RealmObject는 거의 POJO처럼 이용가능하다.
- Realm에서 관리되는 객체를 만드려면,
createObject()
나copyToRealm()
메서드를 이용해야 한다.
제한
final
,transient
,volatile
키워드를 지원하지 않는다.RealmObject
대신 다른 클래스를 상속받는 클래스는 허용되지 않는다.- 기본생성자(인자가 없는 생성자)는 반드시 비어있어야 한다.
Relationship
- RealmObject는 서로 연결될 수 있다.
- 일반적인 Join 연산과 다르게, Realm에서는 일반적으로 가벼운 연산에 속한다.
- has-a 관계이다.
Many - to - 1
- 메인 RealmObject 서브클래스 모델에, 연결할 RealmObject의 서브클래스를 필드로 추가한다.
public class Email extends RealmObject {
private String address;
private boolean active;
// ... setters and getters left out
}
public class Contact extends RealmObject {
private String name;
private Email email;
// ... setters and getters left out
}
- 위 예제에서, 하나의 Contract 객체는 하나의 Email객체를 가진다. 반면에, 하나의 Email 객체는 여러 Contract 객체에 속할 수 있다.
- 종종
1-to-1
로도 이용된다. - RealmObject 필드에 null 값을 넣어서 연결을 끊을 수 있다. 연결이 끊어져도 객체는 그대로 DB에 남아있다.
Many - to - Many
- 메인 RealmObject 서브클래스 모델에,
RealmList<T>
타입의 필드를 추가한다.
public class Contact extends RealmObject {
public String name;
public RealmList<Email> emails;
}
public class Email extends RealmObject {
public String address;
public boolean active;
}
RealmList
는 RealmObject의 서브클래스만 포함할 수 있다.- 자바의 List와 메서드가 동일하다.
- 재귀 관계도 가능하다.
- RealmList에 null값을 지정하면 리스트는 지워진다. 리스트가 지워져도 객체는 그대로 DB에 남아있다.
- RealmList는 절대 null을 반환하지 않는다. 반환된 리스트는 비어있는(size == 0) 리스트이다.
Link Query
- Relationship이 있는 객체를 연결하여 쿼리할 수 있다.
- 관계가 있는 필드의 속성을 조회하려면
.
구분자를 이용해 경로를 명시한다.- 조건을 거는 메서드로는
equalTo
,lessThen
,greaterThen
등을 이용한다.
- 조건을 거는 메서드로는
public class Person extends RealmObject {
private String id;
private String name;
private RealmList<Dog> dogs;
// getters and setters
}
public class Dog extends RealmObject {
private String id;
private String name;
private String color;
// getters and setters
}
- 위의 예시에서, 색깔이 Brown인 강아지를 가지고 있는 사람을 조회하려면 다음과 같다.
// persons => [U1,U2]
RealmResults<Person> persons = realm.where(Person.class)
.equalTo("dogs.color", "Brown")
.findAll();
equalTo
메서드를 연달아 사용하면 기본 쿼리에서 And 를 표현할 수 있다.
// r1 => [U1,U2]
RealmResults<Person> r1 = realm.where(Person.class)
.equalTo("dogs.name", "Fluffy")
.equalTo("dogs.color", "Brown")
.findAll();
- 쿼리 결과내에서 쿼리를 다시 진행하려면 다음과 같이 빌더 패턴을 이용한다.
// r2 => [U2]
RealmResults<Person> r2 = realm.where(Person.class)
.equalTo("dogs.name", "Fluffy")
.findAll()
.where()
.equalTo("dogs.color", "Brown")
.findAll();
.where()
.equalTo("dogs.color", "Yellow")
.findAll();
Write
- 읽기 연산은 아무때나 가능하지만 쓰기연산은
transaction 안에서만 가능
하다. - 쓰기 transaction은 모두 저장되거나, 모두 롤백된다.
// Obtain a Realm instance
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
//... add or update objects here ...
realm.commitTransaction();
// Transaction을 취소하려면 다음과 같이 실행
// realm.cancelTransaction();
- Write Transaction은 다른 동작을 block하게 된다. 그러므로 UI와 background 스레드에서 동시에 Write Transaction을 수행하면 ANR 에러가 발생한다.
- 이를 해결하기 위해 async transaction을 이용
- 읽기 연산은 block되지 않는다.
- Realm은 Exception이 발생해도 이를 내부적으로 처리하기 때문에 크래쉬가 발생하지 않는다. 그러므로 적절하게 transaction을 취소하는 처리가 필요하다.
오브젝트 생성
- RealmObject는 Realm 클래스와 강하게 연관되어 있어서, Realm 클래스를 통해서 생성하는 것을 원칙으로 한다.
- Realm 클래스를 통해 생성하려면
createObject
메서드를 이용한다.
- Realm 클래스를 통해 생성하려면
realm.beginTransaction();
User user = realm.createObject(User.class); // Create a new object
user.setName("John");
user.setEmail("john@corporation.com");
realm.commitTransaction();
- RealmObject의 확장성을 고려해, 일반 생성자를 이용해 객체를 먼저 생성한 후, Realm 클래스에 등록하는 방법도 있다.
- Realm 클래스에 등록하려면
copyToRealm
메서드를 이용한다. - 여러 타입의 생성자를 지원하지만, 생성자를 커스터마이징 하려면 반드시 기본생성자를 구현하되, 생성자 내부는 아무 동작도 처리되지 않도록 한다.
- Realm 클래스에 등록하려면
- Realm 클래스에 등록하고 나면, 처음 생성한 object는 더이상 영구적으로 관리되지 않는다.
copyToRealm
메서드를 통해 반환받은 object만 영구적으로 관리된다.
User user = new User("John");
user.setEmail("john@corporation.com");
// Copy the object to Realm. Any further changes must happen on realmUser
realm.beginTransaction();
User realmUser = realm.copyToRealm(user);
realm.commitTransaction();
Transaction Block
executeTransaction
메서드를 이용하면,beginTransaction
,commitTransaction / cancelTransaction
을 대체할 수 있다.
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
User user = realm.createObject(User.class);
user.setName("John");
user.setEmail("john@corporation.com");
}
});
Asynchronous Transactions
- 트렌젝션들은 다른 트렌젝션에 의해 블락되므로, Realm 트렌젝션은 백그라운드에서 실행할 필요가 있다.
OnSuccess
,OnError
콜백을 통해 트렌젝션이 완료된 후의 콜백을 받는다.- 콜백은
Looper
에 의해 관리되므로, Looper가 있는 스레드에서만 이용할 수 있다.
- 콜백은
realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm bgRealm) {
User user = bgRealm.createObject(User.class);
user.setName("John");
user.setEmail("john@corporation.com");
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
// Transaction was a success.
}
}, new Realm.Transaction.OnError() {
@Override
public void onError(Throwable error) {
// Transaction failed and was automatically canceled.
}
});
executeTransactionAsync()
메서드는RealmAsyncTask
객체를 반환한다. 이 객체를 이용하면 트렌젝션이 완료되지 않았을 때, 원하는 시점에 cancel할 수 있다.
class MyActivity extends Activity {
...
private RealmAsyncTask transaction = realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm bgRealm) {
User user = bgRealm.createObject(User.class);
user.setName("John");
user.setEmail("john@corporation.com");
}
}, null);
...
@Override
public void onStop() {
super.onStop();
if (transaction != null && transaction.isCancelled()) {
transaction.cancel();
}
}
...
}
String & byte arrays 갱신
- Realm은 String 이나 byte array의 개별 원소들의 갱신이 불가능하다.
- 스레드간의 데이터 무결성을 지키기 위해 전체 String, byte array가 하나의 단위로 움직인다.
- 다음의 예시는 byte array의 5번째 원소를 변경하는 예제이다.
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
bytes[] bytes = realmObject.binary;
bytes[4] = 'a';
realmObject.binary = bytes;
}
});
Query
- Realm 객체의
where()
메서드를 이용해RealmQuery
객체를 얻을 수 있다. RealmQuery
클래스는 체이닝 메서드를 지원하므로 편한 방법으로 쿼리문을 만들 수 있고, 마지막으로findAll()
메서드를 호출하여RealmResults
객체를 얻는다.findAll()
메서드는 쿼리결과가 같더라도 항상 새로운RealmResults
객체를 반환한다.- 다음은 User 클래스에서 name이 Jone이거나 Peter 인 객체를 찾는 예제이다.
public class User extends RealmObject {
@PrimaryKey
private String name;
private int age;
@Ignore
private int sessionId;
// Standard getters & setters generated by your IDE…
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public int getSessionId() { return sessionId; }
public void setSessionId(int sessionId) { this.sessionId = sessionId; }
}
// Build the query looking at all users:
RealmQuery<User> query = realm.where(User.class);
// Add query conditions:
query.equalTo("name", "John");
query.or().equalTo("name", "Peter");
// Execute the query:
RealmResults<User> result1 = query.findAll();
// Or alternatively do the same all at once (the "Fluent interface"):
RealmResults<User> result2 = realm.where(User.class)
.equalTo("name", "John")
.or()
.equalTo("name", "Peter")
.findAll();
RealmResults
클래스는AbstractList
클래스를 상속받은 클래스로, List와 사용방법이 비슷하다.- 쿼리결과가 없을 경우에는 빈 Result(
size() == 0
)가 반환된다. RealmResults
객체를 수정하거나 변경하려면 반드시 Write Transaction 안에서 수행해야 한다.
조건
- 다음과 같은 조건들이 있다.
- 범위비교 : between(), greaterThan(), lessThan(), greaterThanOrEqualTo(), LessThanOrEqualTo()
- 동등 : equalTo(), notEqualTo()
- 문자열 : contains(), beginsWidth(), endsWidth()
- Null : isNull(), isNotNull()
- Empty : isEmpty(), isNotEmpty()
Modifiers
- 문자열 조건은
Case.INSENSITIVE
키워드를 이용해 대소문자를 무시할 수 있다.
논리연산
- 여러 조건이 있을 때, 각 조건은 기본적으로
&&
연산으로 결합된다. ||
연산은 명시적으로or()
메서드를 호출해야 한다.beginGroup()
,endGroup()
메서드를 이용해(
,)
를 표현할 수 있다.
RealmResults<User> r = realm.where(User.class)
.greaterThan("age", 10) //implicit AND
.beginGroup()
.equalTo("name", "Peter")
.or()
.contains("name", "Jo")
.endGroup()
.findAll();
정렬
- 정렬을 하려면 쿼리를 수행한 후,
RealmResults
객체의sort()
메서드를 이용해 정렬한다.
RealmResults<User> result = realm.where(User.class).findAll();
result = result.sort("age"); // Sort ascending
result = result.sort("age", Sort.DESCENDING);
체이닝 쿼리
RealmResults
객체를 다시 쿼리하여 결과를 얻을 수 있다.
RealmResults<Person> teenagers = realm.where(Person.class).between("age", 13, 20).findAll();
Person firstJohn = teenagers.where().equalTo("name", "John").findFirst();
- 객체 간에 Relationship 관계가 있을 경우, 그 Relationship이 있는 필드를 쿼리하는 것도 가능하다.
RealmResults<Person> teensWithPups = realm.where(Person.class).between("age", 13, 20).equalTo("dogs.age", 1).findAll();
- 체이닝 쿼리는
RealmResults
클래스에서 제공하는 기능이다.RealmQuery
클래스에서 조건을 추가한다면, 그건 체이닝 쿼리가 아니고 기존 쿼리문을 수정하는 것이다.
자동 업데이트
RealmResults
객체는 자동으로 변경된 값이 적용되기 때문에, 다시 쿼리할 필요가 없다.RealmChangeListener
를 등록하여, 값이 변경되는 시점의 콜백을 받을 수 있다.- 값 변경에 따라 UI를 변경해야 할 경우, 이 시점에서 업데이트 해준다.
final RealmResults<Dog> puppies = realm.where(Dog.class).lessThan("age", 2).findAll();
puppies.size(); // => 0
realm.executeTransaction(new Realm.Transaction() {
@Override
void public execute(Realm realm) {
Dog dog = realm.createObject(Dog.class);
dog.setName("Fido");
dog.setAge(1);
}
});
puppies.addChangeListener(new RealmChangeListener() {
@Override
public void onChange(RealmResults<Dog> results) {
// results and puppies point are both up to date
results.size(); // => 1
puppies.size(); // => 1
}
});
- 자동으로 결과가 변경되기 때문에, 결과의 인덱스나 갯수는 변경될 수 있으므로 사용할 때 유의해야 한다.
Aggregation
RealmResults
클래스는 필드의 합,평균 등을 구할 수 있는 메서드를 가지고 있다.
RealmResults<User> results = realm.where(User.class).findAll();
long sum = results.sum("age").longValue();
long min = results.min("age").longValue();
long max = results.max("age").longValue();
double average = results.average("age");
long matches = results.size();
Iterations
RealmResults
클래스는Iterable
이므로 다음 두가지 방법으로 for문을 이용할 수 있다.
RealmResults<User> results = realm.where(User.class).findAll();
for (User u : results) {
// ... do something with the object ...
}
for (int i = 0; i < results.size(); i++) {
User u = results.get(i);
// ... do something with the object ...
}
- 반복문을 이용할 때, 결과 자체를 수정하거나 삭제하지 않고, 결과의 값을 수정하거나 삭제하면 무결성이 깨져서 앱이 종료될 수 있으므로 주의한다.
// 앱이 죽는 예
final RealmResults<User> users = getUsers();
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
users.get(0).deleteFromRealm(); // indirectly delete object
}
});
for (User user : users) {
showUser(user); // Will crash for the deleted user
}
// 올바른 접근
final RealmResults<User> users = getUsers();
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
users.deleteFromRealm(0); // Delete and remove object directly
}
});
for (User user : users) {
showUser(user); // Deleted user will not be shown
}
삭제
- 다음의 예시처럼, 쿼리 결과를 Realm으로부터 삭제할 수 있다.
// obtain the results of a query
final RealmResults<Dog> results = realm.where(Dog.class).findAll();
// All changes to data must happen in a transaction
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
// remove single match
results.deleteFirstFromRealm();
results.deleteLastFromRealm();
// remove a single object
Dog dog = results.get(5);
dog.deleteFromRealm();
// remove a single object using other way
results.deleteFromRealm(5);
// Delete all matches
results.deleteAllFromRealm();
}
});
비동기 쿼리
- 대부분의 쿼리는 UI 스레드에서 실행해도 될 정도로 빠르지만, 데이터가 많거나 복잡한 쿼리의 경우 백그라운드 스레드로 실행해야 한다.
- 비동기 쿼리를 실행하는 스레드는 반드시
Looper
가 존재해야 한다.(없을 경우IllegalStateException
발생)
쿼리 생성
findAll()
대신findAllAsync()
메서드를 이용한다.findAllAsync()
메서드는 바로 리턴되며, 쿼리의 추가 작업은 백그라운드 스레드에서 계속 진행될 것이다. 작업이 완료되면 자동으로RealmResults
객체를 갱신해준다.- 결과가 로드되었는지 확인하려면
isLoaded()
메서드를 이용한다. (비동기 쿼리가 아닌 경우에는 무조건 true를 반환한다.)
RealmResults<User> result = realm.where(User.class)
.equalTo("name", "John")
.or()
.equalTo("name", "Peter")
.findAllAsync();
if (result.isLoaded()) {
// Result are now available
}
Callback 등록
RealmChangeListener
인터페이스를 이용하면, 비동기 쿼리가 완료되었을 때의 콜백을 받을 수 있다.RealmResults
에addChangeListener()
메서드로 등록하고,removeChangeListener()
메서드로 해제한다.
class MyActivity extends Activity {
...
private RealmChangeListener callback = new RealmChangeListener() {
@Override
public void onChange(RealmResults<User> results) {
// called once the query complete and on every update
}
};
...
@Override
public void onStart() {
super.onStart();
RealmResults<User> result = realm.where(User.class).findAllAsync();
result.addChangeListener(callback);
}
@Override
public void onStop() {
super.onStop();
result.removeChangeListener(callback); // remove a particular listener
// or
result.removeChangeListeners(); // remove all registered listeners
}
...
}
'App > Android' 카테고리의 다른 글
안드로이드 Image To String (0) | 2019.09.02 |
---|---|
안드로이드에서 asynctask의 생명주기 (0) | 2019.07.22 |
[안드로이드] 키보드와 상관없이 특정 뷰를 화면 하단에 고정하는 방법 (0) | 2019.07.18 |
Tmap Api vs Naver Maps Api 가격 비교 (0) | 2019.07.17 |
안드로이드 X Migration (0) | 2019.07.17 |