Realm 使用說明

Realm

一個跨平臺移動數(shù)據(jù)庫引擎

資料

Github

官網(wǎng)

說明文檔

中文說明文檔1.0.0

stetho 官網(wǎng)

stetho-realm Github

導(dǎo)入

  • 配置項目的 build.gradle
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "io.realm:realm-gradle-plugin:2.1.1"
    }
}
  • 配置模組的 build.gradle
apply plugin: 'realm-android'

混淆

不需要

基本用法

模型

繼承RealmObject,或者實現(xiàn)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;
}

// 接口+注解,創(chuàng)建的托管對象缺少生成的部分方法,使用 RealmObject 的靜態(tài)方法替代
@RealmClass
public class Cat implements RealmModel {
    public String name;
}

初始化

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

Realm實例

Realm 實例是線程單例化的,也就是說多次在同一線程調(diào)用靜態(tài)構(gòu)建器會返回同一 Realm 實例。

// Context.getFilesDir() 目錄下的 default.realm 文件
Realm realm = Realm.getDefaultInstance();

RealmConfiguration config = new RealmConfiguration.Builder().build(); // 默認的 RealmConfiguration
Realm.setDefaultConfiguration(configuration); // 設(shè)置默認 RealmConfiguration

// 配合 Configuration 使用
Realm.deleteRealm(configuration); // 清除數(shù)據(jù)
Realm realm = Realm.getInstance(configuration); // 獲取自定義的 Realm
RealmConfiguration config = new RealmConfiguration.Builder()
        .name("myrealm.realm")  // 庫文件名
        .encryptionKey(getKey())  // 加密
        .schemaVersion(42)  // 版本號
        .modules(new MySchemaModule())  // 結(jié)構(gòu)
        .migration(new MyMigration())  // 遷移
        .build();

// 非持久化的、存在于內(nèi)存中的 Realm 實例
RealmConfiguration myConfig = new RealmConfiguration.Builder()
    .name("myrealm.realm")
    .inMemory()
    .build();

事務(wù)

所有的寫操作(添加、修改和刪除對象),必須包含在寫入事務(wù)中,確保線程安全。如果一個寫入事務(wù)正在進行,那么其他的線程的寫入事務(wù)就會阻塞它們所在的線程,使用異步事務(wù)以避免阻塞

讀取事務(wù)是隱式的,讀操作可在任何時候進行。當(dāng)寫入事務(wù)被提交到 Realm 時,該 Realm 的所有其他實例都將被通知,讀入隱式事務(wù)將自動刷新你每個 Realm 對象。

realm.beginTransaction(); // 開始事務(wù)
realm.commitTransaction(); // 提交事務(wù)
realm.cancelTransaction(); // 取消事務(wù)

// 自動處理寫入事務(wù)的開始和提交,并在錯誤發(fā)生時取消寫入事務(wù)
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        // ...
    }
});

// 異步事務(wù),4種重載,onSuccess 和 onError 不是必須,非 Looper 線程中只有空(null)回調(diào)函數(shù)被允許使用
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() {
        // 事務(wù)成功,Looper 傳回前臺執(zhí)行
    }
}, new Realm.Transaction.OnError() {
    @Override
    public void onError(Throwable error) {
        // 事務(wù)失敗,自動取消,Looper 傳回前臺執(zhí)行
    }
});

// 退出注意取消事務(wù)
public void onStop () {
  if (transaction != null && !transaction.isCancelled()) {
      transaction.cancel();
  }
}

添加

User realmUser = realm.createObject(User.class);

// 有主鍵需要添加主鍵,主鍵無自增
User realmUser = realm.createObject(User.class, primaryKeyValue);

// 普通對象轉(zhuǎn)化為托管對象,建議有主鍵的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();
        // 使用以下方法,可避免自動更新集合前,某些元素有可能不在集合內(nèi),引起的崩潰
        results.deleteFromRealm(0);

        // 刪除集合的首末對象
        results.deleteFirstFromRealm();
        results.deleteLastFromRealm();

        // 刪除所有集合內(nèi)對象
        results.deleteAllFromRealm();

        // 刪除所有
        realm.delete(User.class);
        realm.deleteAll();
    }
});

修改

直接修改托管對象,即修改了數(shù)據(jù)庫。
bean 有主鍵時,可使用copyToRealmOrUpdate()轉(zhuǎn)化相同主鍵的對象為托管來修改數(shù)據(jù)庫。

查詢

  • 查詢條件

    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(); // 執(zhí)行查詢
    
    User user = realm.where(User.class)
            .equalTo("name", "John", Case.INSENSITIVE)  // 忽略大小寫
            .findFirst(); // 執(zhí)行查詢
    
  • 關(guān)聯(lián)查詢

    realmresults<user> users = realm.where(user.class)
           .equalto("dogs.name", "fluffy")  // 關(guān)聯(lián)查詢,以“.”分隔
           .equalto("dogs.color", "brown")  // 條件與
           .findall();
    
    realmresults<user> users = realm.where(user.class)
           .equalto("dogs.name", "fluffy")
           .findall()
           .where()  // 在結(jié)果中繼續(xù)查詢
           .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來傳遞查詢結(jié)果。在沒有 Looper 的線程中使用異步查詢會導(dǎo)致 IllegalStateException異常被拋出。

Listener 只工作于 Looper 線程。對于非 Looper 線程請使用Realm.waitForChange()

private RealmResults<User> results;

public void onStart() {
    realm = Realm.getDefaultInstance();
    // 立刻返回一個 RealmResults<User>,當(dāng)其完成時,RealmResults 實例會被更新
    results = realm.where(User.class).findAllAsync();

    realm.addChangeListener(listener); // Realm 注冊監(jiān)聽
    results.addChangeListener(listener); // 結(jié)果注冊監(jiān)聽

    if (results.isLoaded()) {
      // 完成加載執(zhí)行
    }

    results.load(); // 阻塞線程指導(dǎo)異步完成

}

public void onStop () {
  realm.removeChangeListener(listener); // Realm移除監(jiān)聽
  realm.removeAllChangeListeners(); // Realm移除所有監(jiān)聽

  results.removeChangeListener(listener); // 結(jié)果移除監(jiān)聽
  results.removeChangeListeners(); // 結(jié)果移除所有監(jiān)聽
}

private RealmChangeListener listener = new RealmChangeListener<RealmResults<User>>() {
  @Override
  public void onChange(RealmResults<User> results) {
      // 在 Looper 線程,每次更新后執(zhí)行
      // 非 Looper 線程,使用 Realm.waitForChange()
  }
};

關(guān)閉

Realm 實例是基于引用計數(shù)的, 調(diào)用getInstance()獲取了幾次實例,就需要調(diào)用close()關(guān)閉幾次

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();
            }
        }
    }
}
// AsyncTask
protected 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();

注意

  • 基本數(shù)據(jù)類型永遠不能為空,RealmObject數(shù)據(jù)類型永遠可以為空
  • 目前不支持finaltransientvolatile修飾的成員變量
  • 支持使用遞歸關(guān)系,但要注意死循環(huán)的問題, Realm 不會檢查RealmList的循環(huán)嵌套
  • 設(shè)置一個類型為RealmList的屬性為空值(null)會清空該列表,即列表長度變?yōu)?。但并不會刪除列表中的任何RealmObject
  • 在沒有 Looper 的線程中使用異步查詢會導(dǎo)致IllegalStateException異常被拋出。

進階用法

JSON

JSON 包含空值(null)屬性,創(chuàng)建更新對象,對象屬性不可為空時拋出異常

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

某些數(shù)據(jù)模型在編譯期是無法獲得的。例如在處理數(shù)據(jù)遷移(migration)或CSV文件的時候,此時使用 DynamicRealm 可以在沒有 RealmObject 子類的情況下操作 Realm 數(shù)據(jù)

RealmConfiguration realmConfig = new RealmConfiguration.Builder().build();
DynamicRealm realm = DynamicRealm.getInstance(realmConfig);

// 創(chuàng)建 DynamicRealmObject 實例
DynamicRealmObject user = realm.createObject("User");

// 通過字符串訪問數(shù)據(jù),而不是 RealmObject 的定義
String name = person.getString("name");
int age = person.getInt("age");

// DynamicRealm 會忽略 schema、migration 以及 schema 版本的檢查,但結(jié)構(gòu)依然存在。獲取不存在的屬性會報異常
person.getString("I don't exist");

// 查詢工作相同
RealmResults<DynamicRealmObject> users = realm.where("User")
    .equalTo("name", "John")
    .findAll();

schema 結(jié)構(gòu)

  • Realm 使用所有項目中的 Realm 模型類來創(chuàng)建 schema。但這個行為是可以改變的,例如,你可以通過使用 RealmModule 讓 Realm 只包含所有模型類的一個子集。
@RealmModule(classes = { User.class, Dog.class })
public class MyModule {
}

RealmConfiguration config = new RealmConfiguration.Builder()
        .modules(new MyModule())  // 設(shè)置使用的 schema
        .build();

RealmConfiguration config = new RealmConfiguration.Builder()
        .modules(new MyModule(), new MyOtherModule())  // 可以設(shè)置多個 schema
        .build();
  • 在庫中使用到的 Realm 必須通過 RealmModule 來暴露和使用其 schema。
// 庫必須使用 library = true,以阻止默認創(chuàng)建。
// allClasses = true,即為使用所有
@RealmModule(library = true, allClasses = true)
public class MyLibraryModule {
}

// 庫需要確切的設(shè)置 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();

數(shù)據(jù)庫升級

  • 不保存舊數(shù)據(jù)

    RealmConfiguration config = new RealmConfiguration.Builder()
        .deleteRealmIfMigrationNeeded()
        .build()
    
  • 數(shù)據(jù)遷移

    RealmConfiguration config = new RealmConfiguration.Builder()
        .schemaVersion(2)  // 結(jié)構(gòu)改變時增加,默認初始值為0
        .migration(migration)  // 數(shù)據(jù)遷移方案
        .build()
    
    RealmMigration migration = new RealmMigration() {
      @Override
      public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
    
         // 動態(tài) Realm 獲取數(shù)據(jù)庫結(jié)構(gòu)
         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字節(jié))的密鑰參數(shù)給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相關(guān)

  • 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) {
            // 后臺子線程,獲取使用并關(guān)閉 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 的調(diào)用是獨立的事件。你可以注冊監(jiān)聽器的調(diào)用不會返回失敗,但他們永遠不會被觸發(fā)。

    public class OrdersIntentService extends IntentService {
        public OrdersIntentService(String name) {
            super("OrdersIntentService");
        }
    
        @Override
        protected void onHandleIntent(Intent intent) {
            // 后臺子線程,獲取使用并關(guān)閉 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 獲取的對象轉(zhuǎn)換成 Realm 對象
realm.beginTransaction();
List<Repo> realmRepos = realm.copyToRealmOrUpdate(repos);
realm.commitTransaction();

RxJava

RealmRealmResultsRealmObjectDynamicRealmDynamicRealmObject可以轉(zhuǎn)化為Observable

Realm realm = Realm.getDefaultInstance();
GitHubService api = retrofit.create(GitHubService.class);
// 組合 Realm, Retrofit 和 RxJava (使用 Retrolambda),
realm.where(Person.class)
        .isNotNull("username")
        .findAllAsync()
        .asObservable()  // 轉(zhuǎn)化為 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 轉(zhuǎn)化成 RealmProxy
@Parcel(implementations = { UserRealmProxy.class },
        value = Parcel.Serialization.BEAN,
        analyze = { User.class })
public class User extends RealmObject {
    // ...
}
  • 如果你的模型包含 RealmList,那么你需要注冊一個特殊 adapter
  • 一旦對象被打包(parcelled),它將變?yōu)橐粋€有當(dāng)前數(shù)據(jù)快照,不再被 Realm 管理的一個 unmanaged 對象。之后該對象的數(shù)據(jù)變化不會被 Realm 寫入。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 介紹 Realm 是一個 MVCC (多版本并發(fā)控制)數(shù)據(jù)庫,由Y Combinator公司在2014年7月發(fā)布一...
    帶心情去旅行閱讀 64,526評論 34 134
  • 目錄 Getting StartedGetting HelpModelsRelationshipsWritesQu...
    Jafir閱讀 8,370評論 2 4
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,993評論 19 139
  • 聽說最近外賣快送員很火,是不是很久沒看到香港黑幫大片了,看看這些你會覺得,紅星的馬仔改頭換面了,:)~(比喻而已)...
    大海的喜歡閱讀 206評論 0 0
  • 跟好久沒聯(lián)系的初中好友們聯(lián)系上了 建了一個聊天群 熊寶寶跟我說突然想起了初中 因為我們共同的初中同學(xué)一個學(xué)霸今天在...
    辛夷柴胡法半夏閱讀 187評論 0 0