目录
Getting Started
Getting Help
Models
Relationships
Writes
Queries
Realms
Threading
Schemas
JSON
Notifications
Migrations
Encryption
Working With Android
Other Libraries
Testing and Debugging
Current Limitations
Best Practices
Recipes
FAQ
Api Reference
开始
先决条件
我们现在只支持Android里面用到的Java语法
Android Studio >= 1.5.1
最新的Android sdk版本
JDK version >=7.
我们支持从API 9(2.3姜饼)以上的版本
安装
Realm 是被作为一个gradle插件来安装的
安装Realm为gradle插件需要2步
- 第一步:加入下面class path dependency到工程目录下的build.gradle文件中
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "io.realm:realm-gradle-plugin:1.1.0"
}
}
工程目录下的build.gradle文件在这里
工程目录下的build.gradle
- 第二步:在应用目录下的build.gradle文件中添加 realm-android 插件到application的顶部
apply plugin: 'realm-android'
application级别的build.gradle在这
应用目录下的build.gradle
一旦这两个有所改动,简单地刷新一下你的gradle dependencies就可以了。如果你从一个更高的版本到v0.88版本提升,那么你也需要clean一下你的gradle工程(命令./gradlew clean)来移除任何之前缓存的安装的东西
想查看两个文件build.gradle的简单例子在这里
其他build systems
maven 或者ant 构建系统,不支持。如果你想看关于对这些构建系统的支持,下面有,请向我们表明你的兴趣,我们会权衡而定
在是否实现ant和maven支持上,你的意见是很有决定性的
1.0.0版本之后,eclipse是不支持的,请你去用Android studio
混淆
一个混淆配置在中realm library已经支持。这就意味着你不需要加入任何realm 混淆规则进混淆文件配置里面。(意思就是说,你不用混淆了,少操这份心)
Realm Browser
Realm Browser
你可以可以使用menu item tools->Generate demo databases集成一个带用例的测试数据库demo
API参考
例子
获得帮助
模型
Realm model类的创建需要继承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; }
}
一个Realm model类也支持public 、protected、private变量和自定义方法
public class User extends RealmObject {
public String name;
public boolean hasLongName() {
return name.length() > 7;
}
@Override
public boolean equals(Object o) {
// Custom equals comparison
}
}
变量类型
Field types
Realm支持这些类型:boolean, byte, short, int, long, float, double, String, Date and byte[]。在realm中,integer类型 byte、short、int和long其实都是最终映射为long类型。除此之外,RealmObject的子类和RealmList<? extends RealmObject>都支持模型类的关联(就是可以成员变量是关联的其他类,而不仅仅是基础类型)
包装类Boolean,Byte,Short,Integer,Long, Float and Double都可以在model类里面使用。使用这些类,也可以置空,设置值为null
非空变量和null值
在某些情况下,null不是一个恰当的变量值。@Required注解能够检测并报null值的错误。只有Boolean, Byte, Short, Integer, Long, Float, Double, String, byte[] and Date 能被@Required注解。当其他类型用@Required注解的时候编译会失败。主键类型的变量和RealmList类型的变量自身就是非空的、必要的(意思就是主键和RealmList肯定已经不能为空了,已经有Required属性了)。RealmObject的变量是可为空的
ignoring 属性
@Ignore注解表明一个变量不是必要的,是可有可无的。Ignore变量在你写入你model之外的其他属性而你又不想在太多特别的情况下去处理这些没有太大用处的属性的时候是很有用的(意思就是,那些是可忽略的,比如购物车里面每个东西的价格是必要的,但是总价就不是必要的,因为可以推算出来,那么总结这个属性就是可以ignore的,不需要写入数据库)
Auto-Updating Objects
RealmObject是动态的、自动更新到底层的数据去的,这意味着你不需要刷新,就能修改。影响查询的修改会被立马反射到结果中去。(这里涉及多线程的问题,一边在修改一边在查询,只要你这边修改了,那边插叙的结果就是最新修改的,这就是动态的、自动更新的意思)
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
所有RealmObject的和RealmResults的性能不仅让Realm快和高效,而且还让你的代码变得更简单更灵活。例如,如果你的Activity或者Fragment关联了一个RealmObject或者RealmResult实例,在更新UI的时候你不需要担心刷新或者重新获取
Indexing属性
Indexing properties
The annotation @Index will add a search index to the field. This will make inserts slower and the data file larger but queries will be faster. So it’s recommended to only add index when optimizing specific situations for read performance. We support indexing: String, byte, short, int, long, boolean and Date fields.
Primary keys
To promote a field to primary key, you use the annotation @PrimaryKey, and the field type has to be either string (String) or integer (byte, short, int, or long) and its boxed variants (Byte, Short, Integer, and Long). It is not possible to use multiple fields (compound key) as a primary key. Using a string field as a primary key implies that the field is indexed (the annotation @PrimaryKey implicitly sets the annotation @Index).
Using primary keys makes it possible to use the copyToRealmOrUpdate() method, which will look for an existing object with this primary key, and update it if one is found; if none is found, it will create a new object instead. When calling copyToRealmOrUpdate() on classes without primary keys, an exception will be thrown.
Using primary keys has an effect on the performance. Creating and updating object will be a little slower while querying is expected to be a bit faster. It is hard to give numbers as the changes in performance depend on the size of your dataset.
When calling Realm.createObject(), it will return a new object with all fields set to the default value. In this case, there might be a conflict with an existing object whose primary key field is the default value. To avoid this, it is suggested to create an unmanaged object, set values of the fields, and then copy it to Realm by copyToRealm() method.
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);
}
});
For String (String) and boxed integer (Byte, Short, Integer, and Long), Primary keys can have the value null unless the @PrimaryKey annotation is additionally combined with @Required annotation.
Customizing Objects
It is possible to use RealmObjects almost like POJOs. Extending from RealmObject, you can let the fields be public, and use simple assignments instead of setters and getter. An example of such a model class is:
public class Dog extends RealmObject {
public String name;
public int age;
}
You can use Dog like any other class. In order to create a managed Dog object in Realm, you can use the createObject() or copyToRealm() methods.
realm.executeTransaction(new Realm.Transaction() {
@Overrride
public void execute(Realm realm) {
Dog dog = realm.createObject(Dog.class);
dog.name = "Fido";
dog.age = 5;
}
};
You can add logic to your setters and getters if that fits your needs better. This can be useful if you wish to validate values before storing them in your Realm. Moreover, you can easily add custom methods to your RealmObjects.
Limitations
Currently there’s no support for final, transient and volatile fields. This is mainly to avoid discrepancies between how an object would behave as managed by Realm or unmanaged.
Realm model classes are not allowed to extend any other object than RealmObject. If declared, the default constructor (constructor with no parameters) must always be empty. The reason is that a default contructor will call methods which assume a Realm instance is present. But that instance isn’t create before the contructor returns. You can add other constructors for your convienence.
RealmModel interface
An alternative to extending the RealmObject base class is implementing the RealmModel interface and adding the @RealmClass annotation.
@RealmClass
public class User implements RealmModel {
}
All methods available on RealmObject are then available through static methods.
// With RealmObject
user.isValid();
user.addChangeListener(listener);
// With RealmModel
RealmObject.isValid(user);
RealmObject.addChangeListener(user, listener);
Relationships
Any two RealmObjects can be linked together.
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
}
Relationships are generally cheap in Realm. This means that following a link is not expensive in terms of speed, and the internal presentation of relationships is highly efficient in terms of memory consumption.
Many-to-One
Simply declare a property with the type of one of you RealmObject subclasses:
public class Contact extends RealmObject {
private Email email;
// Other fields…
}
Each contact (instance of Contact) have either 0 or 1 email (instance of Email). In Realm, nothing prevent you from using the same email object in multiple contacts, and the model above can be a many-to-one relationship but often used to model one-to-one relationships.
Setting the RealmObject field to null will clear the reference but the object will not be deleted from the Realm.
Many-to-Many
You can establish a relationship to any number of objects from a single object via a RealmList<T> field declaration. For example, consider a contact with multiple email addresses:
public class Contact extends RealmObject {
public String name;
public RealmList<Email> emails;
}
public class Email extends RealmObject {
public String address;
public boolean active;
}
RealmLists are basically containers of RealmObjects, and a RealmList behaves very much like a regular Java List. There are no limitations in Realm to use the same object twice (or more) in different RealmLists, and you can use this to model both one-to-many, and many-to-many relationships.
You can create objects, and use RealmList.add() to add the Email objects to the Contact object:
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Contact contact = realm.createObject(Contact.class);
contact.name = "John Doe";
Email email1 = realm.createObject(Email.class);
email1.address =
email1.active = true;
contact.emails.add(email1);
Email email2 = realm.createObject(Email.class);
email2.address =
email2.active = false;
contact.emails.add(email2);
}
});
It is possible to declare recursive relationships which can be useful when modeling certain types of data.
public class Person extends RealmObject {
public String name;
public RealmList<Person> friends;
// Other fields…
}
Setting the value to null for a RealmList field will clear the list. That is, the list will be empty (length zero), but no objects have been deleted. The getter for a RealmList will never return null. The returned object is always a list but the length might be zero.
Link queries
It is possible to query links or relationships. Consider the model below:
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
}
Each Person object has multiple dog relationships as shown in this table diagram:
Table Diagram
Let’s find some persons with link queries …
// persons => [U1,U2]
RealmResults<Person> persons = realm.where(Person.class)
.equalTo("dogs.color", "Brown")
.findAll();
First of all, notice that the field name in the equalsTo condition contains the path through the relationships (separated by period .).
The query above should read, find all Persons who have dogs who are ‘Brown’. It is important to understand that the result will contain the Dog objects which do not fulfill the condition since they are part of the Person’s object:
persons.get(0).getDogs(); // => [A,B]
persons.get(1).getDogs(); // => [B,C,D]
This can be further examined by the following two queries.
// r1 => [U1,U2]
RealmResults<Person> r1 = realm.where(Person.class)
.equalTo("dogs.name", "Fluffy")
.findAll();
// r2 => [U1,U2]
RealmResults<Person> r2 = r1.where()
.equalTo("dogs.color", "Brown")
.findAll();
Notice how the first query returned both Person objects because the condition matched both persons. Each Person in the query result contains a list of Dog objects - all of their dog objects (even ones that do not fulfill the original query condition). Remember, we’re searching for people who have particular kinds of dogs (names and colors), not the actual dogs themselves. Therefore, the second query will be evaluated against the first Person query result (r1) and each of the Persons dogs. The second query also matches both persons as well, but this time it’s because of the color of the dog.
Let’s dig a little deeper to help solidify this concept. Please review the following example:
// 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();
The first query should read, find all Persons who have dogs named ‘Fluffy’ and also find all Persons who have dogs who are ‘Brown’ and then give me the intersection of the two. The second query should read, find all Persons who have dogs named ‘Fluffy’. Then, given that result set, find all Persons who have dogs whose color is ‘Brown’ and given that result set find all Persons who have dogs whose color is ‘Yellow’.
Let’s take a look at the query behind r1 to fully understand what is happening. The two conditions are equalTo("dogs.name", "Fluffy") and equalTo("dogs.color", "Brown"). The first condition is fulfilled for U1 and U2 - this is set C1. The second condition is also fulfilled for U1 and U2 - this is set C2. The logical-and in the query is the same as an intersection of the two sets C1 and C2. The intersection between C1 and C2 is U1 and U2. Therefore, r1 is U1 and U2.
The query behind r2 is different. Let’s begin by breaking this query apart. The first portion of the query looks like this: RealmResults<Person> r2a = realm.where(Person.class).equalTo("dogs.name", "Fluffy").findAll();. It matches U1 and U2. Then, r2b = r2a.where().equalTo("dogs.color", "Brown").findAll(); also matches U1 and U2 (both persons have brown dogs). The final query, r2 = r2b.where().equalTo("dogs.color", "Yellow").findAll(); matches only U2 since the only person in the brown dog result set that has a Yellow dog is U2.
Writes
Read operations are implicit which means that objects can be accessed and queried at any time. All write operations (adding, modifying, and removing objects) must be wrapped in write transactions. A write transaction can either be committed or cancelled. During the commit, all changes will be written to disk, and the commit will only succeed if all changes can be persisted. By cancelling a write transaction, all changes will be discarded. Using write transactions, your data will always be in a consistent state.
Write transactions are also used to ensure thread safety:
// Obtain a Realm instance
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
//... add or update objects here ...
realm.beginTransaction();
User user = realm.createObject(User.class);
// ...
realm.cancelTransaction();
Please note that write transactions block each other. This can cause ANR errors if you are creating write transactions on both the UI and background threads at the same time. To avoid this, use async transactions when creating write transactions on the UI thread.
Thanks to Realm’s MVCC architecture, reads are not blocked while a write transaction is open! This means that unless you need to make simultaneous transactions from many threads at once, you can favor larger transactions that do more work over many fine-grained transactions. When you commit a write transaction to a Realm, all other instances of that Realm will be notified, and be updated automatically.
Read & write access in Realm is ACID.
Creating objects
Because RealmObjects are strongly tied to a Realm, they should be instantiated through the Realm directly:
public void onStop () {
if (transaction != null && !transaction.isCancelled()) {
transaction.cancel();
}
}
Updating strings and byte arrays
Realm is working on entire fields, and it is not possible to update individual elements of strings or byte arrays. Suppose you need to update the 5th element of a string, you will have to do something like
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
bytes[] bytes = realmObject.binary;
bytes[4] = 'a';
realmObject.binary = bytes;
}
});
This is due to Realm’s MVCC architecture which avoids mutating existing data in place to ensure that other threads or processes reading the data see it in a consistent state.
Queries
All fetches (including queries) are lazy in Realm, and the data is never copied.
Realm’s query engine uses a Fluent interface to construct multi-clause queries.
Using the User class -
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; }
}
To find all users named John or Peter you would write:
// 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();
This gives you a new instance of the class RealmResults, containing the users with the name John or Peter. Objects are not copied - you get a list of references to the matching objects, and you work directly with the original objects that matches your query. The RealmResults inherits from Java’s AbstractList, and behaves in similar ways. For example, RealmResults are ordered, and you can access the individual objects through an index.
When a query does not have any matches, the returned RealmResults object will not be null, but the size() method will return 0.
If you wish modify or delete any of the objects in a RealmResults, you must do so in a write transaction.