Realm 使用说明

AllW23 7年前
   <h2><strong>Realm</strong></h2>    <p>一个跨平台移动数据库引擎</p>    <h2><strong>导入</strong></h2>    <ul>     <li> <p>在项目的 build.gradle 添加:</p> <pre>  <code class="language-java">buildscript {      repositories {          jcenter()      }      dependencies {          classpath "io.realm:realm-gradle-plugin:2.1.1"      }  }</code></pre> </li>     <li> <p>在模组的 build.gradle 中添加:</p> <pre>  <code class="language-java">apply plugin: 'realm-android'</code></pre> </li>     <li> <p>ProGuard</p> <p>不需要针对 Realm 做 ProGuard 的改动</p> </li>    </ul>    <h2><strong>基本用法</strong></h2>    <h3><strong>模型</strong></h3>    <p>继承 RealmObject ,或者实现 RealmModel 接口并添加注解 @RealmClass 。</p>    <pre>  <code class="language-java">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 的静态方法替代  @RealmClass  public class Cat implements RealmModel {      public String name;  }</code></pre>    <h3><strong>初始化</strong></h3>    <pre>  <code class="language-java">// Application 中初始化  Realm.init(context);</code></pre>    <h3><strong>Realm实例</strong></h3>    <p>Realm 实例是线程单例化的,也就是说多次在同一线程调用静态构建器会返回同一 Realm 实例。</p>    <pre>  <code class="language-java">// Context.getFilesDir() 目录下的 default.realm 文件  Realm realm = Realm.getDefaultInstance();    RealmConfiguration config = new RealmConfiguration.Builder().build(); // 默认的 RealmConfiguration  Realm.setDefaultConfiguration(configuration); // 设置默认 RealmConfiguration    // 配合 Configuration 使用  Realm.deleteRealm(configuration); // 清除数据  Realm realm = Realm.getInstance(configuration); // 获取自定义的 Realm</code></pre>    <pre>  <code class="language-java">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();</code></pre>    <h3><strong>事务</strong></h3>    <p>所有的写操作(添加、修改和删除对象),必须包含在写入事务中,确保线程安全。如果一个写入事务正在进行,那么其他的线程的写入事务就会阻塞它们所在的线程,使用异步事务以避免阻塞</p>    <p>读取事务是隐式的,读操作可在任何时候进行。当写入事务被提交到 Realm 时,该 Realm 的所有其他实例都将被通知,读入隐式事务将自动刷新你每个 Realm 对象。</p>    <pre>  <code class="language-java">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();    }  }</code></pre>    <h3><strong>添加</strong></h3>    <pre>  <code class="language-java">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); // 主键冲突时更新,无主键报异常</code></pre>    <h3><strong>删除</strong></h3>    <pre>  <code class="language-java">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();      }  });</code></pre>    <h3><strong>修改</strong></h3>    <p>直接修改托管对象,即修改了数据库。</p>    <p>bean 有主键时,可使用 copyToRealmOrUpdate() 转化相同主键的对象为托管来修改数据库。</p>    <h3><strong>查询</strong></h3>    <ul>     <li> <p>查询条件</p> <pre>  <code class="language-java">between()、greaterThan()、lessThan()、greaterThanOrEqualTo()、lessThanOrEqualTo()  equalTo()、notEqualTo()  contains()、beginsWith()、endsWith()  isNull()、isNotNull()  isEmpty()、isNotEmpty()</code></pre> <pre>  <code class="language-java">RealmResults<User> result = realm.where(User.class)          .between("age", 0, 99)          .findAll(); // 执行查询    User user = realm.where(User.class)          .equalTo("name", "John", Case.INSENSITIVE)  // 忽略大小写          .findFirst(); // 执行查询</code></pre> </li>     <li> <p>关联查询</p> <pre>  <code class="language-java">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();</code></pre> </li>     <li> <p>逻辑运算符</p> <pre>  <code class="language-java">or()、beginGroup()、endGroup()</code></pre> <pre>  <code class="language-java">RealmResults<User> results = realm.where(User.class)        .greaterThan("age", 10)  // 大于等于        .beginGroup()  // 左括号        .equalTo("name", "Peter")        .or()  // 或,如果不加此操作符,默认为于        .contains("name", "Jo")        .endGroup()  // 左右括号        .findAll();</code></pre> </li>     <li> <p>排序</p> <pre>  <code class="language-java">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); // 降序</code></pre> </li>     <li> <p>聚合</p> <pre>  <code class="language-java">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();</code></pre> </li>     <li> <p>异步</p> </li>    </ul>    <p>步查询需要使用Handler来传递查询结果。在没有 Looper 的线程中使用异步查询会导致 IllegalStateException 异常被抛出。</p>    <p>Listener 只工作于 Looper 线程。对于非 Looper 线程请使用 Realm.waitForChange()</p>    <pre>  <code class="language-java">private RealmResults<User> results;      public void onStart() {        realm = Realm.getDefaultInstance();        // 立刻返回一个 RealmResults<User>,当其完成时,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<RealmResults<User>>() {      @Override      public void onChange(RealmResults<User> results) {          // 在 Looper 线程,每次更新后执行          // 非 Looper 线程,使用 Realm.waitForChange()      }    };</code></pre>    <h3><strong>关闭</strong></h3>    <p>Realm 实例是基于引用计数的, 调用 getInstance() 获取了几次实例,就需要调用 close() 关闭几次</p>    <p>UI 线程外的 Looper 线程</p>    <pre>  <code class="language-java">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();              }          }      }  }</code></pre>    <pre>  <code class="language-java">// AsyncTask  protected Void doInBackground(Void... params) {      Realm realm = Realm.getDefaultInstance();      try {          // ...      } finally {          realm.close();      }        return null;  }</code></pre>    <pre>  <code class="language-java">new Thread(new Runnable() {      @Override      public void run() {          Realm realm = null;          try {              realm = Realm.getDefaultInstance();              // ...          } finally {              if (realm != null) {                  realm.close();              }          }      }  }).start();</code></pre>    <h3><strong>注意</strong></h3>    <ul>     <li>基本数据类型永远不能为空, RealmObject 数据类型永远可以为空</li>     <li>目前不支持 final 、 transient 和 volatile 修饰的成员变量</li>     <li>支持使用递归关系,但要注意死循环的问题, Realm 不会检查 RealmList 的循环嵌套</li>     <li>设置一个类型为 RealmList 的属性为空值(null)会清空该列表,即列表长度变为0。但并不会删除列表中的任何 RealmObject</li>     <li>在没有 Looper 的线程中使用异步查询会导致 IllegalStateException 异常被抛出。</li>    </ul>    <h2><strong>进阶用法</strong></h2>    <h3><strong>JSON</strong></h3>    <p>JSON 包含空值(null)属性,创建更新对象,对象属性不可为空时抛出异常</p>    <p>Json 和对象的属性不同的,对象属性不变</p>    <pre>  <code class="language-java">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();          }      }  });</code></pre>    <h3><strong>DynamicRealm</strong></h3>    <p>某些数据模型在编译期是无法获得的。例如在处理数据迁移(migration)或CSV文件的时候,此时使用 DynamicRealm 可以在没有 RealmObject 子类的情况下操作 Realm 数据</p>    <pre>  <code class="language-java">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();</code></pre>    <h3><strong>schema 结构</strong></h3>    <ul>     <li> <p>Realm 使用所有项目中的 Realm 模型类来创建 schema。但这个行为是可以改变的,例如,你可以通过使用 RealmModule 让 Realm 只包含所有模型类的一个子集。</p> </li>    </ul>    <pre>  <code class="language-java">@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();</code></pre>    <ul>     <li> <p>在库中使用到的 Realm 必须通过 RealmModule 来暴露和使用其 schema。</p> </li>    </ul>    <pre>  <code class="language-java">// 库必须使用 library = true,以阻止默认创建。  // allClasses = true,即为使用所有  @RealmModule(library = true, allClasses = true)  public class MyLibraryModule {  }    // 库需要确切的设置 RealmModule  RealmConfiguration libraryConfig = new RealmConfiguration.Builder()    .name("library.realm")    .modules(new MyLibraryModule())    .build();    // Apps 中添加库的 RealmModule  RealmConfiguration config = new RealmConfiguration.Builder()    .name("app.realm")    .modules(Realm.getDefaultModule(), new MyLibraryModule())    .build();</code></pre>    <h3><strong>数据库升级</strong></h3>    <ul>     <li> <p>不保存旧数据</p> <pre>  <code class="language-java">RealmConfiguration config = new RealmConfiguration.Builder()      .deleteRealmIfMigrationNeeded()      .build()</code></pre> </li>     <li> <p>数据迁移</p> <pre>  <code class="language-java">RealmConfiguration config = new RealmConfiguration.Builder()      .schemaVersion(2)  // 结构改变时增加,默认初始值为0      .migration(migration)  // 数据迁移方案      .build()    RealmMigration migration = new RealmMigration() {    @Override    public 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++;       }    }  }</code></pre> </li>    </ul>    <h3><strong>加密</strong></h3>    <p>Realm 文件可以通过传递一个512位(64字节)的密钥参数给 Realm.getInstance().encryptionKey() 来加密存储在磁盘上</p>    <pre>  <code class="language-java">byte[] key = new byte[64];  new SecureRandom().nextBytes(key);  RealmConfiguration config = new RealmConfiguration.Builder()    .encryptionKey(key)    .build();    Realm realm = Realm.getInstance(config);</code></pre>    <h2><strong>高阶用法</strong></h2>    <h3><strong>Android相关</strong></h3>    <ul>     <li> <p>Adapter</p> <p>ListView 使用 RealmBaseAdapter , RecyclerViews 使用 RealmRecyclerViewAdapter</p> <pre>  <code class="language-java">dependencies {      compile 'io.realm:android-adapters:1.4.0'  }</code></pre> </li>     <li> <p>Intent</p> <p>RealmObject 不能通过 Intent 传递,可以通过传递属性然后再查询</p> </li>     <li> <p>AsyncTask</p> <pre>  <code class="language-java">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查询对象,进行操作      }  }</code></pre> </li>     <li> <p>IntentService</p> <p>ChangeListener 在 IntentService 中不会工作。尽管 IntentService 本身是一个 Looper 线程,但每次 onHandleIntent 的调用是独立的事件。你可以注册监听器的调用不会返回失败,但他们永远不会被触发。</p> <pre>  <code class="language-java">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();      }  }</code></pre> </li>    </ul>    <h3><strong>Retrofit</strong></h3>    <pre>  <code class="language-java">GitHubService service = restAdapter.create(GitHubService.class);  List<Repo> repos = service.listRepos("octocat");    // Retrofit 获取的对象转换成 Realm 对象  realm.beginTransaction();  List<Repo> realmRepos = realm.copyToRealmOrUpdate(repos);  realm.commitTransaction();</code></pre>    <h3><strong>RxJava</strong></h3>    <p>Realm 、 RealmResults 、 RealmObject 、 DynamicRealm 、 DynamicRealmObject 可以转化为 Observable</p>    <pre>  <code class="language-java">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));</code></pre>    <h3><strong>Parceler</strong></h3>    <pre>  <code class="language-java">compile "org.parceler:parceler-api:1.0.3"  apt "org.parceler:parceler:1.0.3"</code></pre>    <pre>  <code class="language-java">// 项目编译完成,RealmObject 转化成 RealmProxy  @Parcel(implementations = { UserRealmProxy.class },          value = Parcel.Serialization.BEAN,          analyze = { User.class })  public class User extends RealmObject {      // ...  }</code></pre>    <ul>     <li> <p>如果你的模型包含 RealmList,那么你需要注册一个特殊 adapter</p> </li>     <li> <p>一旦对象被打包(parcelled),它将变为一个有当前数据快照,不再被 Realm 管理的一个 unmanaged 对象。之后该对象的数据变化不会被 Realm 写入。</p> </li>    </ul>    <p> </p>    <p>来自:http://www.jianshu.com/p/95f62e739d1b</p>    <p> </p>