Realm 使用说明

来源:互联网 发布:行行 知乎 编辑:程序博客网 时间:2024/06/04 19:33

Realm

一个跨平台移动数据库引擎

资料

Github

官网

说明文档

中文说明文档1.0.0

stetho 官网

stetho-realm Github

导入

  • 在项目的 build.gradle 添加:
buildscript {    repositories {        jcenter()    }    dependencies {        classpath "io.realm:realm-gradle-plugin:2.1.1"    }}
  • 在模组的 build.gradle 中添加:
apply plugin: 'realm-android'
  • 混淆

    不需要

基本用法

模型

继承RealmObject,或者实现RealmModel接口并添加注解@RealmClass

public class User extends RealmObject {    // 主键,可为空,默认已索引,String、byte、short、int、long、Byte、Short、Integer、Long    @PrimaryKey    private long id;    // 非空,Boolean、ByteShort、Integer、Long、Float、Double、String、byte[]、Date    @Required    private String name;    // 索引,String、byte、short、int、long、boolean、Date    @Index    private int age;    @Ignore // 忽略    private int tempReference;    private Dog dog; // 对单    private RealmList<Cat> cats; // 对多    // 省略 get/set 方法}public class Dog extends RealmObject {    public String name;}// 接口+注解,创建的托管对象缺少生成的部分方法,使用 RealmObject 的静态方法替代@RealmClasspublic class Cat implements RealmModel {    public String name;}

初始化

// Application 中初始化Realm.init(context);

Realm实例

Realm 实例是线程单例化的,也就是说多次在同一线程调用静态构建器会返回同一 Realm 实例。

// Context.getFilesDir() 目录下的 default.realm 文件Realm realm = Realm.getDefaultInstance();RealmConfiguration config = new RealmConfiguration.Builder().build(); // 默认的 RealmConfigurationRealm.setDefaultConfiguration(configuration); // 设置默认 RealmConfiguration// 配合 Configuration 使用Realm.deleteRealm(configuration); // 清除数据Realm realm = Realm.getInstance(configuration); // 获取自定义的 Realm
RealmConfiguration config = new RealmConfiguration.Builder()        .name("myrealm.realm")  // 库文件名        .encryptionKey(getKey())  // 加密        .schemaVersion(42)  // 版本号        .modules(new MySchemaModule())  // 结构        .migration(new MyMigration())  // 迁移        .build();// 非持久化的、存在于内存中的 Realm 实例RealmConfiguration myConfig = new RealmConfiguration.Builder()    .name("myrealm.realm")    .inMemory()    .build();

事务

所有的写操作(添加、修改和删除对象),必须包含在写入事务中,确保线程安全。如果一个写入事务正在进行,那么其他的线程的写入事务就会阻塞它们所在的线程,使用异步事务以避免阻塞

读取事务是隐式的,读操作可在任何时候进行。当写入事务被提交到 Realm 时,该 Realm 的所有其他实例都将被通知,读入隐式事务将自动刷新你每个 Realm 对象。

realm.beginTransaction(); // 开始事务realm.commitTransaction(); // 提交事务realm.cancelTransaction(); // 取消事务// 自动处理写入事务的开始和提交,并在错误发生时取消写入事务realm.executeTransaction(new Realm.Transaction() {    @Override    public void execute(Realm realm) {        // ...    }});// 异步事务,4种重载,onSuccess 和 onError 不是必须,非 Looper 线程中只有空(null)回调函数被允许使用RealmAsyncTask transaction = realm.executeTransactionAsync(new Realm.Transaction() {  @Override  public void execute(Realm bgRealm) {    // 异步不能使用外部的 Realm    User user = bgRealm.createObject(User.class);    user.setName("John");    user.setEmail("john@corporation.com");  }}, new Realm.Transaction.OnSuccess() {    @Override    public void onSuccess() {        // 事务成功,Looper 传回前台执行    }}, new Realm.Transaction.OnError() {    @Override    public void onError(Throwable error) {        // 事务失败,自动取消,Looper 传回前台执行    }});// 退出注意取消事务public void onStop () {  if (transaction != null && !transaction.isCancelled()) {      transaction.cancel();  }}

添加

User realmUser = realm.createObject(User.class);// 有主键需要添加主键,主键无自增User realmUser = realm.createObject(User.class, primaryKeyValue);// 普通对象转化为托管对象,建议有主键的bean使用User user = new User();User realmUser = realm.copyToRealm(user); // 主键冲突时报异常User realmUser = realm.copyToRealmOrUpdate(user); // 主键冲突时更新,无主键报异常

删除

final RealmResults<User> results = realm.where(User.class).findAll();realm.executeTransaction(new Realm.Transaction() {    @Override    public void execute(Realm realm) {        // 删除一个托管对象        results.get(0).deleteFromRealm();        // 使用以下方法,可避免自动更新集合前,某些元素有可能不在集合内,引起的崩溃        results.deleteFromRealm(0);        // 删除集合的首末对象        results.deleteFirstFromRealm();        results.deleteLastFromRealm();        // 删除所有集合内对象        results.deleteAllFromRealm();        // 删除所有        realm.delete(User.class);        realm.deleteAll();    }});

修改

直接修改托管对象,即修改了数据库。
bean 有主键时,可使用copyToRealmOrUpdate()转化相同主键的对象为托管来修改数据库。

查询

  • 查询条件

    between()、greaterThan()、lessThan()、greaterThanOrEqualTo()、lessThanOrEqualTo()
    equalTo()、notEqualTo()
    contains()、beginsWith()、endsWith()
    isNull()、isNotNull()
    isEmpty()、isNotEmpty()

    RealmResults<User> result = realm.where(User.class)      .between("age", 0, 99)      .findAll(); // 执行查询User user = realm.where(User.class)      .equalTo("name", "John", Case.INSENSITIVE)  // 忽略大小写      .findFirst(); // 执行查询
  • 关联查询

    realmresults<user> users = realm.where(user.class)     .equalto("dogs.name", "fluffy")  // 关联查询,以“.”分隔     .equalto("dogs.color", "brown")  // 条件与     .findall();realmresults<user> users = realm.where(user.class)     .equalto("dogs.name", "fluffy")     .findall()     .where()  // 在结果中继续查询     .equalto("dogs.color", "brown")     .findall();
  • 逻辑运算符

    or()、beginGroup()、endGroup()

    RealmResults<User> results = realm.where(User.class)    .greaterThan("age", 10)  // 大于等于    .beginGroup()  // 左括号    .equalTo("name", "Peter")    .or()  // 或,如果不加此操作符,默认为于    .contains("name", "Jo")    .endGroup()  // 左右括号    .findAll();
  • 排序

    RealmResults<User> results = realm.where(User.class).findAll();results = result.sort("age"); // 升序results = result.sort("age", Sort.DESCENDING); // 降序RealmResults<User> results = realm.where(User.class)    .findAllSorted("age", Sort.DESCENDING); // 降序
  • 聚合

    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();
  • 异步

步查询需要使用Handler来传递查询结果。在没有 Looper 的线程中使用异步查询会导致 IllegalStateException异常被抛出。

Listener 只工作于 Looper 线程。对于非 Looper 线程请使用Realm.waitForChange()

“`java
private RealmResults results;

public void onStart() {
realm = Realm.getDefaultInstance();
// 立刻返回一个 RealmResults,当其完成时,RealmResults 实例会被更新
results = realm.where(User.class).findAllAsync();

  realm.addChangeListener(listener); // Realm 注册监听  results.addChangeListener(listener); // 结果注册监听  if (results.isLoaded()) {    // 完成加载执行  }  results.load(); // 阻塞线程指导异步完成

}

public void onStop () {
realm.removeChangeListener(listener); // Realm移除监听
realm.removeAllChangeListeners(); // Realm移除所有监听

results.removeChangeListener(listener); // 结果移除监听results.removeChangeListeners(); // 结果移除所有监听

}

private RealmChangeListener listener = new RealmChangeListener

关闭

Realm 实例是基于引用计数的, 调用getInstance()获取了几次实例,就需要调用close()关闭几次

UI 线程外的 Looper 线程

public class MyThread extends Thread {    private Realm realm;    public void run() {        Looper.prepare();        try {            realm = Realm.getDefaultInstance();            //...            Lopper.loop();        } finally {            if (realm != null) {                realm.close();            }        }    }}
// AsyncTaskprotected Void doInBackground(Void... params) {    Realm realm = Realm.getDefaultInstance();    try {        // ...    } finally {        realm.close();    }    return null;}
new Thread(new Runnable() {    @Override    public void run() {        Realm realm = null;        try {            realm = Realm.getDefaultInstance();            // ...        } finally {            if (realm != null) {                realm.close();            }        }    }}).start();

注意

  • 基本数据类型永远不能为空,RealmObject数据类型永远可以为空
  • 目前不支持finaltransientvolatile修饰的成员变量
  • 支持使用递归关系,但要注意死循环的问题, Realm 不会检查RealmList的循环嵌套
  • 设置一个类型为RealmList的属性为空值(null)会清空该列表,即列表长度变为0。但并不会删除列表中的任何RealmObject
  • 在没有 Looper 的线程中使用异步查询会导致IllegalStateException异常被抛出。

进阶用法

JSON

JSON 包含空值(null)属性,创建更新对象,对象属性不可为空时抛出异常

Json 和对象的属性不同的,对象属性不变

realm.executeTransaction(new Realm.Transaction() {    @Override    public void execute(Realm realm) {        Dog dog = realm.createObjectFromJson(Dog.class, "{\"name\": \"dog\"}");    }});realm.executeTransaction(new Realm.Transaction() {    @Override    public void execute(Realm realm) {        try {            InputStream is = getAssets().open("user.json");            realm.createAllFromJson(User.class, is);        } catch (IOException e) {            e.printStackTrace();        }    }});

DynamicRealm

某些数据模型在编译期是无法获得的。例如在处理数据迁移(migration)或CSV文件的时候,此时使用 DynamicRealm 可以在没有 RealmObject 子类的情况下操作 Realm 数据

RealmConfiguration realmConfig = new RealmConfiguration.Builder().build();DynamicRealm realm = DynamicRealm.getInstance(realmConfig);// 创建 DynamicRealmObject 实例DynamicRealmObject user = realm.createObject("User");// 通过字符串访问数据,而不是 RealmObject 的定义String name = person.getString("name");int age = person.getInt("age");// DynamicRealm 会忽略 schema、migration 以及 schema 版本的检查,但结构依然存在。获取不存在的属性会报异常person.getString("I don't exist");// 查询工作相同RealmResults<DynamicRealmObject> users = realm.where("User")    .equalTo("name", "John")    .findAll();

schema 结构

  • Realm 使用所有项目中的 Realm 模型类来创建 schema。但这个行为是可以改变的,例如,你可以通过使用 RealmModule 让 Realm 只包含所有模型类的一个子集。
@RealmModule(classes = { User.class, Dog.class })public class MyModule {}RealmConfiguration config = new RealmConfiguration.Builder()        .modules(new MyModule())  // 设置使用的 schema        .build();RealmConfiguration config = new RealmConfiguration.Builder()        .modules(new MyModule(), new MyOtherModule())  // 可以设置多个 schema        .build();
  • 在库中使用到的 Realm 必须通过 RealmModule 来暴露和使用其 schema。
// 库必须使用 library = true,以阻止默认创建。// allClasses = true,即为使用所有@RealmModule(library = true, allClasses = true)public class MyLibraryModule {}// 库需要确切的设置 RealmModuleRealmConfiguration libraryConfig = new RealmConfiguration.Builder()  .name("library.realm")  .modules(new MyLibraryModule())  .build();// Apps 中添加库的 RealmModuleRealmConfiguration config = new RealmConfiguration.Builder()  .name("app.realm")  .modules(Realm.getDefaultModule(), new MyLibraryModule())  .build();

数据库升级

  • 不保存旧数据

    RealmConfiguration config = new RealmConfiguration.Builder()  .deleteRealmIfMigrationNeeded()  .build()
  • 数据迁移

    RealmConfiguration config = new RealmConfiguration.Builder()  .schemaVersion(2)  // 结构改变时增加,默认初始值为0  .migration(migration)  // 数据迁移方案  .build()RealmMigration migration = new RealmMigration() {@Overridepublic void migrate(DynamicRealm realm, long oldVersion, long newVersion) {   // 动态 Realm 获取数据库结构   RealmSchema schema = realm.getSchema();   // 迁移版本 1: 增加一个类   // Example:   // public User extends RealmObject {   //     private String name;   //     private int age;   //     // getters and setters left out for brevity   // }   if (oldVersion == 0) {      schema.create("User")          .addField("name", String.class)          .addField("age", int.class);      oldVersion++;   }   // 迁移版本 2: 增加一个主键和对象引用   // Example:   // public Person extends RealmObject {   //     @PrimaryKey   //     private long id;   //     private String name;   //     private int age;   //     private Dog favoriteDog;   //     private RealmList<Dog> dogs;   //     // getters and setters left out for brevity   // }   if (oldVersion == 1) {      schema.get("User")          .addField("id", long.class, FieldAttribute.PRIMARY_KEY)  // 增加主键熟悉“id”          .addRealmObjectField("favoriteDog", schema.get("Dog"))  // 增加对象          .addRealmListField("dogs", schema.get("Dog")); // 增加对象列表      oldVersion++;   }}}

加密

Realm 文件可以通过传递一个512位(64字节)的密钥参数给Realm.getInstance().encryptionKey()来加密存储在磁盘上

byte[] key = new byte[64];new SecureRandom().nextBytes(key);RealmConfiguration config = new RealmConfiguration.Builder()  .encryptionKey(key)  .build();Realm realm = Realm.getInstance(config);

高阶用法

Android相关

  • Adapter

    ListView使用RealmBaseAdapterRecyclerViews使用RealmRecyclerViewAdapter

    dependencies {compile 'io.realm:android-adapters:1.4.0'}
  • Intent

    RealmObject 不能通过 Intent 传递,可以通过传递属性然后再查询

  • AsyncTask

    private class DownloadOrders extends AsyncTask<Void, Void, Long> {  @Override  protected Long doInBackground(Void... voids) {      // 后台子线程,获取使用并关闭 Realm      Realm realm = Realm.getDefaultInstance();      try {          realm.createAllFromJson(Order.class, api.getNewOrders());          Order firstOrder = realm.where(Order.class).findFirst();          long orderId = firstOrder.getId();          return orderId;      } finally {          realm.close();      }  }  @Override  protected void onPostExecute(Long orderId) {      // 返回主线程,通过id查询对象,进行操作  }}
  • IntentService

    ChangeListener 在 IntentService 中不会工作。尽管 IntentService 本身是一个 Looper 线程,但每次 onHandleIntent 的调用是独立的事件。你可以注册监听器的调用不会返回失败,但他们永远不会被触发。

    public class OrdersIntentService extends IntentService {  public OrdersIntentService(String name) {      super("OrdersIntentService");  }  @Override  protected void onHandleIntent(Intent intent) {      // 后台子线程,获取使用并关闭 Realm      Realm realm = Realm.getDefaultInstance();      realm.createAllFromJson(Order.class, api.getNewOrders());      Order firstOrder = realm.where(Order.class).findFirst();      long orderId = firstOrder.getId();      realm.close();  }}

Retrofit

GitHubService service = restAdapter.create(GitHubService.class);List<Repo> repos = service.listRepos("octocat");// Retrofit 获取的对象转换成 Realm 对象realm.beginTransaction();List<Repo> realmRepos = realm.copyToRealmOrUpdate(repos);realm.commitTransaction();

RxJava

RealmRealmResultsRealmObjectDynamicRealmDynamicRealmObject可以转化为Observable

Realm realm = Realm.getDefaultInstance();GitHubService api = retrofit.create(GitHubService.class);// 组合 Realm, Retrofit 和 RxJava (使用 Retrolambda),realm.where(Person.class)        .isNotNull("username")        .findAllAsync()        .asObservable()  // 转化为 Observable        .filter(persons.isLoaded)        .flatMap(persons -> Observable.from(persons))        .flatMap(person -> api.user(person.getGithubUserName()))        .observeOn(AndroidSchedulers.mainThread())        .subscribe(user -> showUser(user));

Parceler

compile "org.parceler:parceler-api:1.0.3"apt "org.parceler:parceler:1.0.3"
// 项目编译完成,RealmObject 转化成 RealmProxy@Parcel(implementations = { UserRealmProxy.class },        value = Parcel.Serialization.BEAN,        analyze = { User.class })public class User extends RealmObject {    // ...}
  • 如果你的模型包含 RealmList,那么你需要注册一个特殊 adapter
  • 一旦对象被打包(parcelled),它将变为一个有当前数据快照,不再被 Realm 管理的一个 unmanaged 对象。之后该对象的数据变化不会被 Realm 写入。
1 0