Realm Java

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公衆號:山青詠芝(shanqingyongzhi)
➤博客園地址:山青詠芝(https://www.cnblogs.com/strengthen/
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:http://www.javashuo.com/article/p-mmtfqzyn-bn.html 
➤若是連接不是山青詠芝的博客園地址,則多是爬取做者的文章。
➤原文已修改更新!強烈建議點擊原文地址閱讀!支持做者!支持原創!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★html

入門

先決條件

  • Android Studio 1.5.1或更高版本
  • JDK 7.0或更高版本
  • 最新版本的Android SDK
  • Android API等級9或更高(Android 2.3及更高版本)

注意: Realm不支持Android以外的Java。咱們再也不支持Eclipse做爲IDE; 請遷移到Android Studio。java

安裝

將Realm安裝爲Gradle插件。react

步驟1:將類路徑依賴項添加到項目級build.gradle文件。linux

buildscript { repositories { jcenter() } dependencies { classpath "io.realm:realm-gradle-plugin:5.12.0" } }

build.gradle此處查找項目級別文件:android

項目級build.gradle文件

第2步:realm-android插件應用到應用程序級build.gradle文件的頂部git

apply plugin: 'realm-android'

build.gradle此處查找應用程序級別文件:github

應用程序級build.gradle文件

完成這兩項更改後,只需刷新gradle依賴項便可。若是您從早期版本的Realm升級v0.88,則可能還須要清理gradle項目(./gradlew clean)。數據庫

build.gradle在此處查找兩個已修改文件的示例npm

您是否但願使用Realm Mobile Platform同步全部Realm數據庫?全部與同步相關的文檔已移至咱們的平臺文檔中編程

其餘構建系統

不支持Maven和Ant構建系統。咱們正在跟蹤在GitHub上支持它們的興趣:

ProGuard配置做爲Realm庫的一部分提供。這意味着您無需向ProGuard配置添加任何Realm特定規則。

樣品

Realm Java容許您以安全,持久和快速的方式有效地編寫應用程序的模型層。這是它的樣子:

// 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數據庫

若是您在查找應用程序的Realm文件時須要幫助,請查看此StackOverflow答案以獲取詳細說明。

Realm Studio

Realm Studio是咱們的首選開發人員工具,能夠輕鬆管理Realm數據庫和Realm平臺。使用Realm Studio,您能夠打開和編輯本地和同步的域,並管理任何Realm Object Server實例。它支持Mac,Windows和Linux。

Realm Studio

Stetho Realm

您還可使用Stetho的Stetho-Realm插件,這是由Facebook建立的Chrome瀏覽器的Android調試橋。

Stetho-Realm並不是由Realm正式維護。

初始化領域

在應用程序中使用Realm以前,必須先將其初始化。這隻須要作一次。

Realm.init(context);

您必須提供Android context初始化Realm的好地方是onCreate應用程序子類:

public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); Realm.init(this); } }

若是您建立本身的應用程序子類,則必須將其添加到應用程序AndroidManifest.xml

<application android:name=".MyApplication" ... />

三界

一個境界是一種境界移動數據庫容器的一個實例。領域能夠是本地的同步的。同步的Realm使用Realm Object Server透明地將其內容與其餘設備同步。當您的應用程序繼續使用同步Realm時,就像它是本地文件同樣,該Realm中的數據可能會被具備該Realm寫入權限的任何設備更新。實際上,您的應用程序能夠以一樣的方式使用任何Realm,本地或同步。

您是否但願使用Realm Mobile Platform同步全部Realm數據庫?全部與同步相關的文檔已移至咱們的平臺文檔中

有關Realms的更詳細討論,請閱讀Realm Data Model

開放的領域

經過實例化一個新Realm對象來打開一個領域咱們已經在示例中看到過這種狀況:

// Initialize Realm Realm.init(context); // Get a Realm instance for this thread Realm realm = Realm.getDefaultInstance();

getDefaultInstance方法使用默認值實例化Realm RealmConfiguration

配置領域

要控制如何建立領域,請使用RealmConfiguration對象。Realm可用的最小配置是:

RealmConfiguration config = new RealmConfiguration.Builder().build();

該配置 - 沒有選項 - 使用default.realm位於的Realm文件Context.getFilesDir要使用其餘配置,您須要建立一個新RealmConfiguration對象:

// 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對象,所以您能夠獨立控制每一個Realm的版本,架構和位置。

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);

使用Realm.getPath獲取Realm的絕對路徑

重要的是要注意Realm實例是線程單例,這意味着靜態構造函數將返回相同的實例以響應來自給定線程的全部調用。

默認領域

RealmConfiguration能夠保存爲默認配置。在自定義Application類中設置默認配置使其在其他代碼中可用。

public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); // The default Realm file is "default.realm" in Context.getFilesDir(); // we'll change it to "myrealm.realm" Realm.init(this); RealmConfiguration config = new RealmConfiguration.Builder().name("myrealm.realm").build(); Realm.setDefaultConfiguration(config); } } public class MyActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Realm realm = Realm.getDefaultInstance(); // opens "myrealm.realm" try { // ... Do something ... } finally { realm.close(); } } }

打開同步領域

您是否但願使用Realm Mobile Platform同步全部Realm數據庫?全部與同步相關的文檔已移至咱們的平臺文檔中

只讀領域

readOnly僅在當前流程中強制執行。其餘進程或設備仍能夠寫入readOnlyRealms。此外,任何針對只讀Realm的寫入事務都將拋出IllegalStateException這包括嘗試編寫模式,所以必須首先由其餘來源提供。

使用您的應用程序發送準備好的Realm文件有時頗有用 - 您可能但願將一些共享數據與您的應用程序捆綁在一塊兒。在許多狀況下,您不但願意外地修改該Realm,由於數據純粹是隻讀的。您能夠經過在資產中捆綁Realm文件並使用readOnly配置來執行此操做

RealmConfiguration config = new RealmConfiguration.Builder() .assetFile("my.realm") .readOnly() // It is optional, but recommended to create a module that describes the classes // found in your bundled file. Otherwise if your app contains other classes // than those found in the file, it will crash when opening the Realm as the // schema cannot be updated in read-only mode. .modules(new BundledRealmModule()) .build();

內存領域

使用inMemory配置,您能夠建立一個徹底在內存中運行而不會持久保存到磁盤的Realm。

RealmConfiguration myConfig = new RealmConfiguration.Builder() .name("myrealm.realm") .inMemory() .build();

若是內存不足,內存領域仍可能使用磁盤空間,但在關閉領域時,內存領域建立的全部文件都將被刪除。不容許建立與持久Realm同名的內存域 - 名稱仍然必須是惟一的。

當具備特定名稱的全部內存中Realm實例超出範圍而沒有引用時,這將釋放全部Realm的數據。要在應用程序執行期間保持內存中的Realm「活着」,請保留對它的引用。

動態領域

使用常規時Realm,使用RealmObject類定義模型類。這在類型安全方面具備不少好處。但有時,類型在運行時纔可用,例如,在遷移期間或使用基於字符串的數據(如CSV文件)時。動態領域的救援!

DynamicRealm是以往的變體Realm,使得它可以以域數據,而無需使用工做RealmObject子類。相反,全部訪問都是使用字符串而不是類來完成的。

打開Dynamic Realm使用與傳統Realm相同的配置,但Dynamic Realm忽略任何已配置的架構,遷移和架構版本。

RealmConfiguration realmConfig = new RealmConfiguration.Builder().build(); DynamicRealm realm = DynamicRealm.getInstance(realmConfig); // In a DynamicRealm all objects are DynamicRealmObjects realm.beginTransaction(); DynamicRealmObject person = realm.createObject("Person"); realm.commitTransaction(); // All fields are accessed using strings String name = person.getString("name"); int age = person.getInt("age"); // An underlying schema still exists, so accessing a field that does not exist // will throw an exception person.getString("I don't exist"); // Queries still work normally RealmResults<DynamicRealmObject> persons = realm.where("Person") .equalTo("name", "John") .findAll();

一個DynamicRealm在兩個類型安全和性能爲代價的收益靈活性; 通常來講,你應該使用普通的領域。只有在須要靈活性時才使用Dynamic Realms。

關閉領域

Realm實現Closeable以處理本機內存釋放和文件描述符,所以在完成它們時老是關閉它們。

Realm實例是引用計數 - 若是你getInstance在一個線程中調用兩次,你也須要調用close兩次。這容許您實現Runnable類而沒必要擔憂哪一個線程將執行它們:只需啓動它getInstance並以結束它close

對於UI線程,最簡單的方法是realm.close在擁有組件的onDestroy方法中執行若是須要建立LooperUI之外線程,可使用如下模式:

public class MyThread extends Thread { private Realm realm; @Override public void run() { Looper.prepare(); realm = Realm.getDefaultInstance(); try { //... Setup the handlers using the Realm instance ... Looper.loop(); } finally { realm.close(); } } }

對於AsyncTask這是一個很好的模式:

protected Void doInBackground(Void... params) { Realm realm = Realm.getDefaultInstance(); try { // ... Use the Realm instance ... } finally { realm.close(); } return null; }

若是您正在使用ThreadRunnable用於短時間任務:

// Run a non-Looper thread with a Realm instance. Thread thread = new Thread(new Runnable() { @Override public void run() { Realm realm = Realm.getDefaultInstance(); try { // ... Use the Realm instance ... } finally { realm.close(); } } }); thread.start();

若是您正在使用minSdkVersion >= 19Java >= 7使用應用程序,那麼您可使用try-with-resources:

try (Realm realm = Realm.getDefaultInstance()) { // No need to close the Realm instance manually }

自動刷新

若是從與Looper關聯的線程獲取Realm實例,則Realm實例會附帶自動刷新功能。(Android的UI線程是一個Looper。)這意味着Realm實例將按期更新到最新版本。這使您能夠絕不費力地使用最新內容不斷更新UI!

若是你從沒有一個線程得到一個領域實例不是有一個Looper鏈接,從該實例對象將不會被直到調用更新waitForChange方法。堅持使用舊版本的數據在內存和磁盤空間方面是昂貴的,而且成本會隨着保留和最新版本之間的版本數量而增長。這就是爲何在線程中完成它後當即關閉Realm實例很重要的緣由。

若是要檢查Realm實例是否已激活自動刷新,請使用該isAutoRefresh方法。

楷模

經過擴展RealmObject基類來建立Realm模型

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; } }

一個領域模型類支持publicprotectedprivate領域,以及自定義方法。

public class User extends RealmObject { public String name; public boolean hasLongName() { return name.length() > 7; } @Override public boolean equals(Object o) { // Custom equals comparison } }

字段類型

境界支持booleanbyteshortintlongfloatdoubleStringDatebyte[]字段類型。整數類型byteshortint,而且long都被映射到long領域內。除了那些標準字段類型以外,Realm還支持子類RealmObjectRealmList<? extends RealmObject>模型關係。

盒裝類型BooleanByteShortIntegerLongFloatDouble也能夠在模型中的類使用。這些類型可能具備價值null

必填字段

@Required註釋能夠用來告訴境界不容許null在一個字段的值,使之須要,而不是可選的。只有BooleanByteShortIntegerLongFloatDoubleStringbyte[]而且Date能夠進行註釋@Required若是將其添加到其餘字段類型,編譯將失敗。

RealmList隱式地須要具備基本類型和類型的字段RealmObject類型的字段始終能夠爲空。

主鍵

要將字段標記爲模型的主鍵,請使用註釋@PrimaryKey字段類型必須是一個字符串(String)或整數(byteshortintlongByteShortInteger,和Long)。使用字符串字段做爲主鍵會自動爲字段編制索引:@PrimaryKey字符串上的註釋會隱式設置註釋@IndexRealm不支持複合鍵,即便用多個字段做爲單個主鍵。

使用主鍵可使用copyToRealmOrUpdateinsertOrUpdate方法。它們查找具備給定主鍵的對象,並更新它(若是具備該鍵的對象已存在)或建立它(若是該鍵不存在)。若是您在沒有主鍵的狀況下調用copyToRealmOrUpdateinsertOrUpdate上課,則會拋出異常。

當您使用主鍵時,讀取(查詢)會稍微快一些,但寫入(建立和更新對象)會慢一些。性能的變化取決於Realm數據集的大小。

請注意,Realm.createObject返回一個新對象,其全部字段都設置爲其默認值。若是對象是具備主鍵的類,則可能會產生衝突 - 可能存在具備該主鍵集的對象。爲避免這種狀況,您能夠建立一個非託管對象,設置其字段值,而後使用copyToRealm將其添加到Realm insert

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); } });

主鍵是String類型或盒裝整數(ByteShortInteger,和Long)的值能夠是null,除非@PrimaryKey註釋與組合@Required

索引屬性

要索引字段,請使用註釋@Index與主鍵同樣,這會使寫入速度稍慢,但會使讀取速度更快。(它還會使您的Realm文件略大,以存儲索引。)最好只在優化特定狀況下的讀取性能時添加索引。

你能夠索引StringbyteshortintlongbooleanDate領域。

忽略屬性

若是您不想將模型中的字段保存到其Realm,請使用註釋@Ignore例如,若是您的輸入包含的字段多於模型,而且您不但願有許多特殊狀況來處理這些未使用的數據字段,則能夠執行此操做。

字段標statictransient老是被忽略,而且不須要@Ignore註釋。

計數器

Realm提供MutableRealmInteger做爲特殊整數類型。MutableRealmInteger公開了一個額外的API,能夠更清楚地表達意圖,並在使用Synchronized Realms時生成更好的衝突解決步驟

傳統上,計數器將經過讀取值,遞增並設置(myObj.counter += 1)來實現。這在異步狀況下沒法正常工做 - 例如,當兩個客戶端處於脫機狀態時 - 由於雙方都會讀取一個值,好比10增長它,並將值存儲爲11最終,當他們從新得到鏈接並嘗試合併他們的更改時,他們會贊成計數器處於11預期狀態而不是預期狀態12

MutableRealmIntegerS被傳統的整數類型的支持,因此從改變字段時就不須要進行遷移byteshortintlongMutableRealmInteger

MutableRealmInteger不是像Java中的原始數字類型那樣的不可變類型標準。這是一個活生生的物體同樣RealmObjectRealmResultsRealmList這意味着MutableRealmInteger當寫入Realm時,包含在其中的值會發生變化。所以,MutableRealmInteger必須標記字段final

public class Party extends RealmObject { { public final MutableRealmInteger guests = MutableRealmInteger.valueOf(0); }

要更改計數器值,只需調用counter.increment()counter.decrement()

Party party = realm.where(Party.class).findFirst(); realm.beginTransaction(); party.guests.get(); // 0 party.guests.increment(1); // 1 party.guests.decrement(1); // 0 party.guests.increment(5); // 5 party.guests.decrement(1); // 4 realm.commitTransaction();

要重置計數器,您可使用分配新值counter.set()

調用set()有可能覆蓋increment()decrement()業務來自其餘設備來公關。正常的last-write-wins合併規則,所以只有在有損計數器能夠接受的狀況下才能進行混合操做。

Party party = realm.where(Party.class).findFirst(); realm.beginTransaction(); party.guests.set(0); realm.commitTransaction();

覆蓋屬性名稱

默認行爲是Realm將使用Java模型類中定義的名稱做爲名稱來表示Realm文件內部的類和字段。在某些狀況下,您可能但願更改此行爲:

  • 支持具備相同簡單名稱但在不一樣包中的兩個模型類。
  • 爲了更容易使用跨平臺模式,由於命名約定是不一樣的。
  • 使用長度超過Realm強制執行的57個字符限制的Java類名。
  • 在Java中更改字段名稱而不強制應用程序用戶完成遷移過程。

在這些狀況下,您能夠覆蓋被定義使用不一樣的名稱在內部使用的名稱@RealmModule@RealmClass@RealmField註解。

您能夠在模塊級別定義命名策略,這將影響模塊的全部類部分:

@RealmModule( allClasses = true, classNamingPolicy = RealmNamingPolicy.LOWER_CASE_WITH_UNDERSCORES, fieldNamingPolicy = RealmNamingPolicy.LOWER_CASE_WITH_UNDERSCORES ) public class MyModule { }

您能夠爲類或字段命名策略定義將影響該類中全部字段的自定義名稱。這將覆蓋任何模塊級別設置:

@RealmClass(name = "__Person", fieldNamingPolicy = RealmNamingPolicy.PASCAL_CASE) public class Person extends RealmObject { public String name; }

您能夠爲字段定義自定義名稱,這將覆蓋任何類和模塊級別設置:

public class extends RealmObject { @RealmField(name = "person_name") public String name; }

選擇與Java模型類中使用的名稱不一樣的內部名稱具備如下含義:

  • DynamicRealm上的查詢必須使用內部名稱。普通Realm實例上的查詢必須繼續使用Java類中定義的名稱。
  • 建立類和字段時,遷移必須使用內部名稱。
  • 報告的架構錯誤將使用內部名稱。

請注意,更改內部名稱不會影響從JSON數據導入。JSON數據仍必須遵循Realm Java類中定義的名稱。

在使用Moshi,GSON或Jackson等標準庫解析JSON時。而後重要的是要記住,這些庫定義了從JSON到Java的轉換,同時設置內部Realm名稱定義了從Java到Realm文件的轉換。這意味着若是要使用這些庫從JSON將數據導入Realm,您仍須要提供JSON解析器庫和Realm的註釋。

使用Moshi,它看起來像這樣:

public class Person extends RealmObject { @Json(name = "first_name") // Name used in JSON input. @RealmField(name = "first_name") // Name used internally in the Realm file. public string firstName; // name used in Java }

有關詳細信息,請參閱RealmNamingPolicy

使用RealmObjects

自動更新對象

RealmObjects是對基礎數據的實時,自動更新視圖; 你永遠沒必要刷新對象。對象的更改會當即反映在查詢結果中。

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的快速和高效,還可使您的代碼更簡單,更具反應性。若是您的Activity或Fragment依賴於特定RealmObjectRealmResults實例,則在更新UI以前無需擔憂刷新或從新獲取它。

您能夠訂閱Realm通知以瞭解Realm數據什麼時候更新。

自定義對象

可使用RealmObject幾乎像POJO擴展您的課程RealmObject您可讓字段公開,而且可使用簡單的分配而不是setter和getter。

public class Dog extends RealmObject { public String name; public int age; }

您能夠Dog使用任何其餘類同樣使用:您能夠爲getter和setter方法添加邏輯(例如,用於驗證),而且能夠添加任何您想要的自定義方法。

要將Dog對象添加到Realm,請使用createObjectcopyToRealm方法:

realm.executeTransaction(new Realm.Transaction() { @Overrride public void execute(Realm realm) { Dog dog = realm.createObject(Dog.class); dog.name = "Fido"; dog.age = 5; } };

RealmModel接口

RealmObject您的類能夠實現RealmModel接口,而不是擴展添加@RealmClass註釋:

@RealmClass public class User implements RealmModel { }

使用此接口,RealmObject可經過靜態方法得到全部可用的方法。請注意,擴展的類RealmObject不須要@RealmClass註釋或實現RealmModel

// With RealmObject user.isValid(); user.addChangeListener(listener); // With RealmModel RealmObject.isValid(user); RealmObject.addChangeListener(user, listener);

JSON

您能夠添加映射RealmObject到Realm 的JSON對象JSON對象能夠是a StringJSONObjectInputStreamRealm將忽略未定義的JSON中的任何屬性RealmObject經過Realm.createObjectFromJson添加單個對象,並經過[Realm.createAllFromJson] [api / io / realm / Realm.html#createAllFromJson-java.lang.Class-java.lang.String-)添加對象列表。

// A RealmObject that represents a city public class City extends RealmObject { private String city; private int id; // getters and setters left out ... } // Insert from a string realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { realm.createObjectFromJson(City.class, "{ city: \"Copenhagen\", id: 1 }"); } }); // Insert multiple items using an InputStream realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { try { InputStream is = new FileInputStream(new File("path_to_file")); realm.createAllFromJson(City.class, is); } catch (IOException e) { throw new RuntimeException(e); } } });

若是JSON對象中null的字段是,而且Realm模型須要該字段,則Realm將拋出異常。若是該字段是可選字段,則在建立對象時以及null更新對象時,其值將設置爲字段默認值若是Realm模型具備JSON對象中不存在的字段,則該值將在Realm模型中保持不變。

適配器

領域提供抽象的實用工具類,幫助綁定的數據來自何處OrderedRealmCollectionS(二者RealmResultsRealmList實現該接口)標準的UI部件。

要使用適配器,請將依賴項添加到應用程序級別build.gradle

dependencies { compile 'io.realm:android-adapters:2.1.1' }

適配器的Javadoc能夠在這裏找到,能夠在這裏找到它們的使用示例

意圖

因爲RealmObjects不是Parcelable也不能直接傳遞,所以必須爲您正在使用的對象傳遞標識符。例如,若是對象具備主鍵,則傳遞Intent extras包中的主鍵值:

// Assuming we had a person class with a @PrimaryKey on the 'id' field ... Intent intent = new Intent(getActivity(), ReceivingService.class); intent.putExtra("person_id", person.getId()); getActivity().startService(intent);

從接收端的bundle(Activity,Service,IntentService,BroadcastReceiver等)中檢索主鍵值,而後打開一個Realm並查詢RealmObject

// in onCreate(), onHandleIntent(), etc. String personId = intent.getStringExtra("person_id"); Realm realm = Realm.getDefaultInstance(); try { Person person = realm.where(Person.class).equalTo("id", personId).findFirst(); // do something with the person ... } finally { realm.close(); }

在另外一個線程上從新打開Realm的開銷很是小。

您能夠Object Passing線程示例部分中找到工做示例該示例向您展現如何傳遞id並檢索RealmObject常見的Android用例。

關係

您能夠將任意兩個RealmObject連接在一塊兒。Realm中的關係很便宜:遍歷連接在速度或內存方面並不昂貴。讓咱們探索不一樣類型的關係,Realm容許您在對象之間進行定義。

許多到一

要設置多對一或一對一關係,請爲模型提供其類型爲您的RealmObject子類之一的屬性

public class Email extends RealmObject { private String address; private boolean active; } public class Contact extends RealmObject { private String name; private Email email; } Contact bob = realm.createObject(Contact.class); bob.name = "Bob Newhart"; Email email1 = realm.createObject(Email.class); email1.address = "bob@example.com"; bob.email = email1;

每一個Contact都有零個或一個Email實例。什麼都不會阻止你使用多個相同的Email對象Contact多對一和一對一關係之間的區別取決於您的應用程序。

設置關係字段null將清除引用:

bob.email = null;

這將刪除之間的關係bobemail1,但email1仍處於境界。

許多一對多

您能夠經過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; }

RealmLists是s的容器RealmObject一個RealmList行爲就像一個普通的Java List您能夠在不一樣的RealmLists中使用相同的對象,而且可使用它來模擬一對多和多對多關係。

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 = "john@example.com"; email1.active = true; contact.emails.add(email1); Email email2 = realm.createObject(Email.class); email2.address = "jd@example.com"; email2.active = false; contact.emails.add(email2); } });

能夠聲明遞歸關係,這在建模某些類型的數據時很是有用。

public class Person extends RealmObject { public String name; public RealmList<Person> friends; // Other fields... }

將值設置nullRealmList字段將清除列表。列表將爲空(長度爲零),但列表中的對象不會從Realm中刪除。RealmList永遠不會返回a的getter null:返回的對象始終是一個列表。長度可能爲零。

反向關係

關係是單向的。就拿兩個類Person,並Dog做爲一個例子:

public class Dog extends RealmObject { private String name; private int age; } public class Person extends RealmObject { @PrimaryKey private long id; private String name; private RealmList<Dog> dogs; }

您能夠按照從a Person到a 的連接Dog,但沒法從a Dog到其Person對象。您能夠經過爲Dog添加@LinkingObjects註釋來解決此問題

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; @LinkingObjects("dogs") private final RealmResults<Person> owners; // getters and setters }

咱們給Dog了一個owners字段,並指定它應該包含在其字段Person中具備此Dog對象的全部對象dogs

必須聲明帶註釋的字段final,而且必須是類型RealmResults<T>,其中T是關係的另外一端的類型/類。因爲關係是多對一或多對多,所以遵循反向關係可能會產生0,1個或更多對象。

與任何其餘RealmResults集合同樣,您能夠查詢反向關係。

原始列表

領域模型類能夠包含原始數據類型的列表。這必須經過建模RealmList<T>,其中T:能夠是如下類型StringIntegerBooleanFloatDoubleShortLongBytebyte[]Date

public class Person extends RealmObject { public String name; public RealmList<String> children = new RealmList<>(); }

RealmModel列表不一樣,基元列表能夠包含空值。若是不容許使用空值,請使用@Required註釋:

public class Person extends RealmObject { public String name; @Required public RealmList<String> children = new RealmList<>(); }

原語列表不支持列表列表和查詢。

在Realm Java 4.0.0以前,使用特殊Realm<String/Int>對基元列表進行建模是很常見的您可使用如下遷移代碼今後方法遷移到基元列表:

// Model classes public class RealmString extends RealmObject { public String value; } public class Person extends RealmObject { public String name; @Required public RealmList<String> children = new RealmList<>(); } // Migration code RealmObjectSchema objSchema = realmSchema.get("Person"); objSchema.addRealmListField("children_tmp", String.class) .setRequired("children_tmp", true) .transform(new RealmObjectSchema.Function() { @Override public void apply(DynamicRealmObject obj) { RealmList<DynamicRealmObject> children = obj.getList("children"); RealmList<String> migratedChildren = obj.getList("children_tmp", String.class); for (DynamicRealmObject child : children) { migratedChildren.add(child.getString("value")); } } }) .removeField("children") .renameField("children_tmp", "children");

架構

Realm的默認模式只是項目中的全部Realm模型類。可是,您能夠更改此行爲 - 例如,您可能但願將Realm限制爲僅包含類的子集。爲此,請建立自定義RealmModule

// Create the module @RealmModule(classes = { Person.class, Dog.class }) public class MyModule { } // Set the module in the RealmConfiguration to allow only classes defined by the module. RealmConfiguration config = new RealmConfiguration.Builder() .modules(new MyModule()) .build(); // It is possible to combine multiple modules to one schema. RealmConfiguration config = new RealmConfiguration.Builder() .modules(new MyModule(), new MyOtherModule()) .build();

對於庫開發人員:包含Realm的庫必須經過RealmModule公開和使用他們的模式。這樣作能夠防止RealmModule爲庫項目生成默認值,這會違反RealmModule應用程序使用的默認值該庫RealmModule也是庫如何將其Realm類暴露給應用程序。

// A library must create a module and set library = true. This will prevent the default // module from being created. // allClasses = true can be used instead of listing all classes in the library. @RealmModule(library = true, allClasses = true) public class MyLibraryModule { } // Library projects are therefore required to explicitly set their own module. RealmConfiguration libraryConfig = new RealmConfiguration.Builder() .name("library.realm") .modules(new MyLibraryModule()) .build(); // Apps can add the library RealmModule to their own schema. RealmConfiguration config = new RealmConfiguration.Builder() .name("app.realm") .modules(Realm.getDefaultModule(), new MyLibraryModule()) .build();

您不能RealmModule在單個文件中包含多個聲明。若是您有兩個或更多個RealmModules,則必須將聲明拆分爲多個文件,每一個文件只有一個聲明。

在此處查看 RealmModules如何在庫和應用程序項目之間工做的完整示例

與讀取操做不一樣,Realm中的寫入操做必須包含在事務中。在寫入操做結束時,您能夠提交事務或取消它。提交事務會將全部更改寫入磁盤(若是同步了Realm,則將其排隊以與Realm Object Server同步)。若是取消寫入事務,則會丟棄全部更改。事務是「全有或全無」:事務中的全部寫入都成功,或者它們都不生效。這有助於保證數據的一致性,並提供線程安全性。

// Obtain a Realm instance Realm realm = Realm.getDefaultInstance(); realm.beginTransaction(); //... add or update objects here ... realm.commitTransaction();

或者經過取消交易來放棄更改:

realm.beginTransaction(); User user = realm.createObject(User.class); // ... realm.cancelTransaction();

寫事務相互阻塞。若是同時在UI和後臺線程上建立寫入事務,則可能致使ANR錯誤。要避免這種狀況,請在UI線程上建立寫入事務時使用異步事務

若是事務內發生異常,您將丟失該事務中的更改,但Realm自己不會受到影響(或損壞)。若是您捕獲異常而且應用程序繼續,則您須要取消該事務。若是使用executeTransaction,則會自動執行此操做。

因爲Realm的MVCC架構,在寫事務打開時不會阻止讀取。除非您須要同時從多個線程同時進行事務,不然您能夠支持更大的事務,這些事務能夠在許多細粒度事務上完成更多工做。當您向Realm提交寫入事務時,該Realm的全部其餘實例將被通知並自動更新

Realm中的讀寫訪問權限是ACID

建立對象

createObject方法包裝在寫入事務中。

realm.beginTransaction(); User user = realm.createObject(User.class); // Create a new object user.setName("John"); user.setEmail("john@corporation.com"); realm.commitTransaction();

若是首先建立對象實例並使用copyToRealm它將其添加到Realm,則應將複製操做包裝在事務中。Realm支持任意數量的自定義構造函數,只要其中一個是公共無參數構造函數便可

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();

請記住,Realm只管理返回的對象(realmUser在本例中),而不是最初複製的對象(user)。要更改數據庫中的對象,請更改返回的副本,而不是原始副本。

若是您只是插入對象而不是當即使用託管副本,則可使用insert這種方式與此相似,copyToRealm但速度要快得多,由於不返回對象能夠更好地對其進行優化。

若是要插入許多對象,建議的方法是使用insertinsertOrUpdate

List<User> users = Arrays.asList(new User("John"), new User("Jane")); realm.beginTransaction(); realm.insert(users); realm.commitTransaction();

交易塊

不用手動保持的跟蹤beginTransactioncommitTransaction以及cancelTransaction,你可使用executeTransaction方法,它會自動處理開始/提交,若是發生錯誤的時候取消。

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"); } });

異步事務

因爲事務被其餘事務阻止,您可能但願在後臺線程上編寫以免阻塞UI線程。經過使用異步事務,Realm將在後臺線程上運行該事務。

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. } });

OnSuccessOnError回調都是可選的,但若是提供,它們將分別在事務成功或失敗時被調用。回調由the控制Looper,所以只容許在Looper線程上使用。

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);

RealmAsyncTask若是您須要在事務完成以前退出Activity / Fragment,對象能夠取消任何掛起的事務。若是回調更新UI,忘記取消事務可能會致使應用程序崩潰!

public void onStop () { if (transaction != null && !transaction.isCancelled()) { transaction.cancel(); } }

更新字符串和字節數組

因爲Realm做爲一個總體在字段上運行,所以沒法直接更新字符串或字節數組的各個元素。相反,您須要讀取整個字段,對單個元素進行修改,而後在事務塊中再次將其寫回。

realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { bytes[] bytes = realmObject.binary; bytes[4] = 'a'; realmObject.binary = bytes; } });

批量更新

若是須要一次更新多個對象,那麼最有效的方法是建立查找對象並使用RealmResults.setX()方法之一的查詢,其中X是要更新的字段類型。

// Updating a boolean field realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { RealResults<Person> persons = realm.where(Person.class).equalTo("invited", false).findAll(); persons.setBoolean("invited", true); } });

也可使用調用的通用set方法RealmResults.setValue(String fieldName, Object value),該方法自動檢測輸入的類型並根據須要進行轉換。這相似於行爲DynamicRealmObject.setX()DynamicRealmObject.setValue()行爲。使用RealmResults.setValue()比使用特定類型的方法慢。

// Updating a boolean field using automatic input conversion as needed. realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { RealResults<Person> persons = realm.where(Person.class).equalTo("invited", false).findAll(); persons.setValue("invited", "true"); } });

只能經過這種方式直接在對象上更新字段。沒法使用這些方法更新連接的字段或列表元素。

查詢

全部提取(包括查詢)在Realm中都是惰性的,而且永遠不會複製數據。

Realm的查詢引擎使用Fluent接口來構造多子句查詢。

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; } }

要查找名爲John或Peter的全部用戶,您須要寫:

// 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,其中包含名爲John或Peter的用戶。

該方法findAll執行查詢; RealmQuery ] []包含一系列findAll方法:

  • findAll 查找知足查詢條件的全部對象
  • findAllAsync 在後臺線程上異步操做
  • findFirst(和findFirstAsync)找到知足查詢條件第一個對象

有關完整的詳細信息,請深刻了解RealmQuery API參考。

查詢返回匹配對象的引用列表,所以您能夠直接使用與查詢匹配的原始對象。RealmResultsAbstractList相似的方式繼承和行爲。例如,按RealmResults順序排列,您能夠經過索引訪問各個對象。若是查詢沒有匹配項,則返回的RealmResults對象將是size(0)(不null的列表

若是要修改或刪除集合中的對象RealmResults,則必須在寫入事務中執行此操做。

請注意,您還能夠查詢關係:閱讀有關連接查詢的信息

過濾

where方法RealmQuery經過指定模型來啓動過濾條件使用謂詞方法指定,其中大多數具備不言自明的名稱(例如,equalTo)。謂詞始終將字段名稱做爲其第一個參數。

並不是全部謂詞均可以與全部字段類型一塊兒使用; 有關詳細信息,請參閱[ RealmQuery ] [] API參考。

對於全部數據類型,您具備如下謂詞:

  • equalTo
  • notEqualTo
  • in

要將字段與值列表進行匹配,請使用in例如,要查找名稱「Jill」,「William」或「Trillian」,您可使用in("name", new String[]{"Jill", "William", "Trillian"})in謂詞是適用於字符串,二進制數據,和數字字段(包括日期)。

數字數據類型(包括Date)容許使用如下附加謂詞:

  • between (包括兩個終點,即它是一個有界區間)
  • greaterThan
  • lessThan
  • greaterThanOrEqualTo
  • lessThanOrEqualTo

字符串字段容許這些附加謂詞:

  • contains
  • beginsWith
  • endsWith
  • like

全部四個字符串謂詞都有一個可選的第三個參數來控制區分大小寫:Case.INSENSITIVECase.SENSITIVE默認是Case.SENSITIVE

謂詞like執行glob樣式的通配符匹配。匹配模式由字符和一個或多個通配符組成:

  • * 匹配0個或更多Unicode字符
  • ? 匹配單個Unicode字符

例如,考慮一個帶有四個對象的Realm,其中一個字段name的值爲William,Bill,Jill和Trillian。謂詞like("name", "?ill*")將匹配前三個對象,並like("name", "*ia?")匹配第一個和最後一個對象。

二進制數據,字符串和RealmObjects(RealmList列表能夠是空的,即長度爲零。您能夠經過如下方式檢查空虛:

  • isEmpty
  • isNotEmpty

若是不須要字段,則該值能夠具備該值null(回想一下,RealmObject永遠不須要s的字段,值能夠是null)。你能夠檢查null

  • isNull
  • isNotNull

邏輯運算符

條件與邏輯隱式鏈接必須使用明確應用邏輯聯接or

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; } }

您還可使用beginGroupendGroup指定評估順序對條件進行分組

RealmResults<User> r = realm.where(User.class) .greaterThan("age", 10) // implicit AND .beginGroup() .equalTo("name", "Peter") .or() .contains("name", "Jo") .endGroup() .findAll();

否認條件not您可使用not運算符和beginGroupendGroup僅取消子條件。若是您想要找到未命名爲「Peter」或「Jo」的用戶,則查詢多是:

RealmResults<User> r = realm.where(User.class) .not() .beginGroup() .equalTo("name", "Peter") .or() .contains("name", "Jo") .endGroup() .findAll();

可是,使用此特定查詢,它更容易使用in

RealmResults<User> r = realm.where(User.class) .not() .in("name", new String[]{"Peter", "Jo"}) .findAll();

排序

您能夠定義在使用該sort方法執行查詢時應如何對結果進行排序

RealmResults<User> result = realm.where(User.class).sort("age").findAll();

或者,您能夠對Realm已檢索的任何結果進行排序:

result = result.sort("age"); // Sort ascending result = result.sort("age", Sort.DESCENDING);

排序默認爲升序; 改變它,Sort.DESCENDING用做第二個參數。能夠同時使用多個字段進行排序。

限制結果

大多數其餘數據庫技術提供了從查詢中「分頁」結果的能力(例如SQLite中的'LIMIT'關鍵字)。這一般是爲了不從磁盤中讀取太多內容,或者一次將太多結果拉入內存中。

因爲Realm中的查詢是惰性的,所以在使用本地Realm文件時一般不須要執行此類分頁,由於Realm只會在顯式訪問後從查詢結果中加載對象。

可是,在某些狀況下限制結果多是有益的:

  • 使用基於查詢的域時,將在查詢時從服務器獲取數據。在這種狀況下,限制結果的數量是有意義的,由於它直接影響從服務器傳輸的數據量。

  • 建立依賴於有限查詢結果的UI時,如「前10名列表」。在這種狀況下,建立有限的查詢結果將下降建立此類屏幕所需的代碼的複雜性。

在這些狀況下,能夠經過將limit()關鍵字應用於查詢來限制查詢結果

RealmResults<Person> people = realm.where(Person.class) .sort("name") .limit(10) .findAllAsync();

與任何其餘查詢結果同樣,有限查詢結果會自動更新這意味着若是在查詢首次返回時返回10個元素,則能夠在基礎數據集更改時替換或刪除這10個對象。

使用細粒度通知時,中止做爲其一部分的對象RealmResults將被報告爲已刪除。這並不必定意味着它們會從底層Realm中刪除,只是它們再也不是查詢結果的一部分。

關鍵字distinct()sort()limit()會在他們指定的順序來應用。根據數據集,這可能會對查詢結果產生影響。通常來講,limit()應該最後應用。

尚不支持抵消有限的查詢結果。此功能正在此處進行跟蹤

獨特的價值觀

要僅返回惟一值,請使用distinct謂詞。例如,要了解您的Realm中有多少個不一樣的名稱:

RealmResults<Person> unique = realm.where(Person.class).distinct("name").findAll();

你只能調用distinct整數和字符串字段; 其餘字段類型將引起異常。與排序同樣,您能夠指定多個字段。

連接查詢

您能夠對結果集運行其餘查詢:

RealmResults<Person> teenagers = realm.where(Person.class).between("age", 13, 20).findAll(); Person firstJohn = teenagers.where().equalTo("name", "John").findFirst();

您也能夠在子對象上連接查詢。假設上面的Person對象有一個Dog對象列表

public class Dog extends RealmObject { private int age; // getters & setters ... } public class Person extends RealmObject { private int age; private RealmList<Dog> dogs; // getters & setters ... }

您能夠查詢年齡在13到20歲之間且至少有一隻一歲的狗的全部人:

RealmResults<Person> teensWithPups = realm.where(Person.class).between("age", 13, 20).equalTo("dogs.age", 1).findAll();

請注意,查詢鏈是基於RealmResults而不是構建的RealmQueryRealmQuery對象添加更多條件時,您正在修改查詢自己。

能夠查詢連接或關係。考慮下面的模型:

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 }

每一個Person對象都有多個狗關係,以下表所示:

表格圖

如今,咱們能夠找到具備連接查詢的特定人員:

// persons => [U1,U2] RealmResults<Person> persons = realm.where(Person.class) .equalTo("dogs.color", "Brown") .findAll();

字段名稱equalTo經過關係路徑,使用句點(.)做爲分隔符。上面的查詢顯示「查找全部擁有顏色爲棕色的狗的人。」請注意,結果將包含至少有一個匹配的對象的全部 Dog對象PersonDog

persons.get(0).getDogs(); // => [A,B] persons.get(1).getDogs(); // => [B,C,D]

請記住,咱們正在尋找擁有特定種類狗的人,而不是真正的狗。

讓咱們深刻挖掘一下:

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

第一個問題是:「找到全部有狗名爲'蓬鬆' 而且有顏色爲'布朗'的狗的人。」第二個問題是:「找到全部有狗名爲'蓬鬆'的人。」 在該結果集中,找到全部擁有顏色爲「棕色」的狗的人。而後,在該結果集中,找到全部擁有顏色爲「黃色」的狗的人。「所以,第一個查詢找到兩組人員並返回這些集合的交集; 第二個查詢以不一樣的方式運行,經過獲取每一個查詢的結果集findAll並將其提供給下一個where查詢以連續縮小結果範圍。您能夠經過連接重寫第二個查詢:

RealmResults<Person> set1 = realm.where(Person.class).equalTo("dogs.name", "Fluffy").findAll(); RealmResults<Person> set2 = set1.where(Person.class).equalTo("dogs.color", "Brown").findAll(); RealmResults<Person> set3 = set2.where(Person.class).equalTo("dogs.color", "Yellow").findAll();

使用反向關係,您能夠擴展查詢的可能性。讓咱們考慮相同的兩個模型類,PersonDogPerson您能夠先查詢狗,而不是使用反向關係,而不是啓動查詢

RealmResults<Dog> brownFluffies = realm.where(Dog.class).equalTo("color", "Brown").equalTo("name", "Fluffy").findAll(); for (Dog brownFluffy : brownFluffies) { RealmResults<Person> owners = brownFluffy.getOwners(); // ... }

您還可使用具備反向關係的連接查詢:

RealmResults<Dog> dogs = realm.where(Dog.class).equalTo("persons.name", "Jane").findAll();

自動更新結果

RealmResults是實時的,自動更新基礎數據的視圖。若是另外一個線程,進程甚至設備修改RealmResults集合中的對象,則當即反映更改。您的代碼無需從新運行查詢或手動刷新數據。

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 } });

這適用於全部RealmResults:全部對象,已過濾和連接。

這種屬性RealmResults不只能夠保持Realm的快速和高效,並且可使您的代碼更簡單,更具反應性。例如,若是Activity或Fragment依賴於查詢結果,則能夠只存儲Realm對象或RealmResults在字段中。訪問時,其數據始終是最新的。

即便您沒必要刷新您RealmResults的應用程序,您的應用程序也可能須要更新其UI或在數據更改時運行其餘任務。您能夠在Realm數據更新時訂閱通知因爲結果是自動更新的,所以不要依賴索引和計數保持不變是很重要的。

聚合

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();

迭代和快照

全部Realm系列都是實時的這意味着它們總能反映最新狀態。在大多數狀況下,這是可取的,可是若是你爲了修改元素而迭代一個集合呢?例如:

RealmResults<Person> guests = realm.where(Person.class).equalTo("invited", false).findAll(); realm.beginTransaction(); for (int i = 0; guests.size(); i++) { guests.get(i).setInvited(true); } realm.commitTransaction();

一般,您會指望這個簡單的循環邀請全部客人。由於RealmResults當即更新,只有一半的客人最終被邀請!邀請客人將當即從集合中刪除,這將移動全部元素。i參數遞增時,它將錯過一個元素。

爲防止這種狀況,您能夠拍攝集合數據快照快照可確保元素的順序不會更改,即便刪除或修改了元素也是如此。

Iterator從中建立的RealmResults將自動使用快照,而不會Iterator建立RealmList要在迭代時刪除元素RealmListIterator.remove()應該使用代替RealmList.remove()或其餘API來RealmList間接刪除元素以免ConcurrentModificationExceptionRealmResultsRealmListcreateSnapshot一個手動建立一個方法。

RealmResults<Person> guests = realm.where(Person.class).equalTo("invited", false).findAll(); // Use an iterator to invite all guests realm.beginTransaction(); for (Person guest : guests) { guest.setInvited(true); } realm.commitTransaction(); // Use a snapshot to invite all guests realm.beginTransaction(); OrderedRealmCollectionSnapshot<Person> guestsSnapshot = guests.createSnapshot(); for (int i = 0; guestsSnapshot.size(); i++) { guestsSnapshot.get(i).setInvited(true); } realm.commitTransaction();

刪除

您能夠從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(); // Delete all matches results.deleteAllFromRealm(); } });

異步查詢

Realm中的大多數查詢都足夠快,即便在UI線程上也能夠同步運行。可是,對於大型數據集上的複雜查詢或查詢,在後臺線程上運行查詢多是一個優點。

RealmResults<User> result = realm.where(User.class) .equalTo("name", "John") .or() .equalTo("name", "Peter") .findAllAsync();

請注意,查詢未阻止 - 它會當即返回a RealmResults<User>這是一個相似於標準Java中Future的概念的承諾查詢將繼續在後臺線程中運行,一旦完成就更新返回的實例RealmResults

若是您但願在查詢完成並RealmResults更新對象時收到通知,則能夠註冊a RealmChangeListener每次RealmResults更新時都會調用此偵聽器以反映Realm中的最新更改(一般在提交以後)。

private OrderedRealmCollectionChangeListener<RealmResults<User>> callback = new OrderedRealmCollectionChangeListener<>() { @Override public void onChange(RealmResults<User> results, OrderedCollectionChangeSet changeSet) { if (changeSet == null) { // The first time async returns with an null changeSet. } else { // Called on every update. } } }; private RealmResults<User> result; public void onStart() { result = realm.where(User.class).findAllAsync(); result.addChangeListener(callback); }

請記住在退出活動或片斷時取消註冊任何偵聽器以免內存泄漏。

public void onStop () { result.removeChangeListener(callback); // remove a particular listener // or result.removeAllChangeListeners(); // remove all registered listeners }

使用isLoaded來檢查,若是查詢已完成:

RealmResults<User> result = realm.where(User.class).findAllAsync(); if (result.isLoaded()) { // Results are now available }

調用同步得到isLoadedRealmResults對象將始終返回true

您也能夠等到查詢完成。這將阻止線程,使查詢再次同步。

RealmResults<User> result = realm.where(User.class).findAllAsync(); result.load() // be careful, this will block the current thread until it returns

注意:您只能在Looper線程上使用異步查詢。異步查詢須要使用Realm的Handler才能始終如一地提供結果。嘗試使用在沒有Looper的線程內打開的Realm調用異步查詢將拋出一個IllegalStateException

遷移

使用任何數據庫時,您的模型類(即數據庫模式)可能會隨着時間的推移而發生變化。因爲Realm中的模型類被定義爲標準對象,所以更改模式就像更改相應RealmObject子類的接口同樣簡單

本地遷移

對於未同步到Realm Object Server的領域,執行遷移須要對RealmConfiguration進行兩處更改:設置新的架構版本,並編寫代碼以執行遷移。

RealmConfiguration config = new RealmConfiguration.Builder() .schemaVersion(2) // Must be bumped when the schema changes .migration(new MyMigration()) // Migration to run instead of throwing an exception .build()

使用此選項,將根據須要自動運行遷移代碼。咱們提供內置方法,以便您能夠升級磁盤上的架構,以及爲先前版本的架構存儲的數據。

// Example migration adding a new class public class MyMigration implements RealmMigration { @Override public void migrate(DynamicRealm realm, long oldVersion, long newVersion) { // DynamicRealm exposes an editable schema RealmSchema schema = realm.getSchema(); // Migrate to version 1: Add a new class. // Example: // public Person extends RealmObject { // private String name; // private int age; // // getters and setters left out for brevity // } if (oldVersion == 0) { schema.create("Person") .addField("name", String.class) .addField("age", int.class); oldVersion++; } // Migrate to version 2: Add a primary key + object references // Example: // public Person extends RealmObject { // private String name; // @PrimaryKey // private int age; // private Dog favoriteDog; // private RealmList<Dog> dogs; // // getters and setters left out for brevity // } if (oldVersion == 1) { schema.get("Person") .addField("id", long.class, FieldAttribute.PRIMARY_KEY) .addRealmObjectField("favoriteDog", schema.get("Dog")) .addRealmListField("dogs", schema.get("Dog")); oldVersion++; } } }

有關更多詳細信息,請參閱遷移示例應用

若是Realm啓動時磁盤上沒有文件,則不須要遷移,Realm將.realm根據代碼中定義的最新模型建立新文件和模式。這意味着,若是您處於開發階段並常常更改架構 - 而且能夠丟失全部數據 - 您能夠刪除.realm磁盤上文件而不是編寫遷移。在應用程序開發週期的早期修補模型時,這會頗有幫助。

RealmConfiguration config = new RealmConfiguration.Builder() .deleteRealmIfMigrationNeeded() .build()

通知

能夠註冊偵聽器以接收有關Realm或其實體的更改的通知。當Realm做爲一個總體被更改時發送領域通知更改,添加或刪除單個對象時會發送收集通知

經過調用removeChangeListenerremoveAllChangeListeners方法中止通知傳遞若是註冊偵聽器的對象被垃圾回收,或者其Realm實例已關閉,則通知也將中止。只要您須要通知,就應該保留對您正在收聽的對象的強引用。

// Wrong way to register for notifications. Query result will // be GC'ed when the method exits causing the listener to stop // emitting notifications. public void runQuery() { realm.where(Person.class) .findAllAsync() .addChangeListener(new RealmChangeListener() { public void onChange(RealmResults<Person> persons) { // Persons was updated } }; } // Right way to register for notifications. The listener will // continue to emit notifications after the method exits. RealmResults<Person> persons; public void runQuery() { persons = realm.where(Person.class) .findAllAsync() .addChangeListener(new RealmChangeListener() { public void onChange(RealmResults<Person> persons) { // Persons was updated } }; }

通知始終在最初註冊的線程上提供。該線程必須有一個正在運行的Looper若是相關的寫入事務發生在另外一個線程上,則在提交事務後將異步調用該偵聽器。

若是寫入事務發生在同一個線程上,則在提交事務時將同步調用偵聽器。可是,在某些狀況下,能夠在事務開始時調用偵聽器- 若是Realm進入最新版本,或者觀察到的Realm實體以觸發通知的方式被修改或刪除。在這些狀況下,偵聽器在當前寫入事務的上下文中運行,所以嘗試在通知處理程序中開始新的寫入事務將引起異常。您可使用該Realm.isInTransaction方法肯定您的代碼是否在寫入事務中執行。

因爲異步通知是經過looper事件傳遞的,所以looper隊列中的其餘事件可能會延遲通知的傳遞。當沒法當即傳遞通知時,多個寫入事務的更改可能會合併爲單個通知。

領域通知

您能夠經過添加一個偵聽器來通知您的UI或其餘循環線程,以便在Realm發生更改時執行該偵聽器:

public class MyActivity extends Activity { private Realm realm; private RealmChangeListener realmListener; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); realm = Realm.getDefaultInstance(); realmListener = new RealmChangeListener<Realm>() { @Override public void onChange(Realm realm) { // ... do something with the updates (UI, etc.) ... } }; realm.addChangeListener(realmListener); } @Override protected void onDestroy() { super.onDestroy(); // Remove the listener. realm.removeChangeListener(realmListener); // Close the Realm instance. realm.close(); } }

Realm上的監聽器接收整個已更改的Realm。

收集通知

收集通知不是整個領域,而是細粒度的更改描述。它們包括自上次通知以來已添加,刪除或修改的對象索引。收集通知是異步傳遞的,首先是初始結果,而後是每次寫入事務後再次發送,這會改變集合中的任何對象(或添加新對象)。

能夠經過OrderedCollectionChangeSet傳遞給更改偵聽器參數來訪問這些更改此對象包含有關受刪除插入更改影響的索引的信息

前兩個,刪除插入,記錄已添加到集合或從集合中刪除的對象的索引。這會將對象添加到Realm或從Realm中刪除它們時考慮在內。爲此,RealmResults當您篩選特定值並更改對象以使其如今與查詢匹配或再也不匹配時也適用。

每當對象的字段發生更改時,您都會收到有關更改的通知,這些更改之前是集合的一部分,而且仍然是其中的一部分。當一對多關係發生變化時,也會發生這種狀況。

public class Dog extends RealmObject { public String name; public int age; } public class Person exteds RealmObject { public String name; public RealmList<Dog> dogs; }

咱們假設您正在觀察上面的模型代碼給出的狗主人名單。您將收到有關匹配的Person對象的修改的通知,例如:

  • 你修改了Person這個名字。
  • 您能夠Dog在屬於a的狗列表中添加或刪除Person
  • 你修改的年齡Dog屬於該Person

這使得能夠離散地控制對UI內部內容進行的動畫和視覺更新,而不是每次發生通知時任意從新加載全部內容。

private final OrderedRealmCollectionChangeListener<RealmResults<Person>> changeListener = new OrderedRealmCollectionChangeListener<RealmResults<Person>>() { @Override public void onChange(RealmResults<Person> collection, OrderedCollectionChangeSet changeSet) { // `null` means the async query returns the first time. if (changeSet == null) { notifyDataSetChanged(); return; } // For deletions, the adapter has to be notified in reverse order. OrderedCollectionChangeSet.Range[] deletions = changeSet.getDeletionRanges(); for (int i = deletions.length - 1; i >= 0; i--) { OrderedCollectionChangeSet.Range range = deletions[i]; notifyItemRangeRemoved(range.startIndex, range.length); } OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges(); for (OrderedCollectionChangeSet.Range range : insertions) { notifyItemRangeInserted(range.startIndex, range.length); } OrderedCollectionChangeSet.Range[] modifications = changeSet.getChangeRanges(); for (OrderedCollectionChangeSet.Range range : modifications) { notifyItemRangeChanged(range.startIndex, range.length); } } };

RealmRecyclerViewAdapter提供這種開箱即用的。

對象通知

Realm支持對象級通知。您能夠在特定項目上註冊通知RealmObject,以便在刪除對象時或在對象上的任何管理字段修改其值時通知您。

只有託管RealmObjects能夠在其上註冊監聽器。

能夠經過ObjectChangeSet傳遞給更改偵聽器參數來訪問這些更改ObjectChangeSet持有哪些領域發生了變化信息,若是RealmObject被刪除。

若是刪除了對象ObjectChangeSet.isDeleted則返回true以後,不會再次調用監聽器。

ObjectChangeSet.getChangedFields若有對象的託管字段被改變將返回改變字段的名稱。您還可使用它ObjectChangeSet.isFieldChanged來測試給定字段是否剛剛更改。

private final RealmObjectChangeListener<Dog> listener = new RealmObjectChangeListener<Dog>() { @Override public void onChange(Dog dog, ObjectChangeSet changeSet) { if (changeSet.isDeleted()) { Log.i(TAG, "The dog was deleted"); return; } for (String fieldName : changeSet.getChangedFields()) { Log.i(TAG, "Field " + fieldName + " was changed."); } } };

相同值的通知

Realm會將全部更改視爲會引起通知的內容。可是,在許多狀況下,若是值未更改,則不但願刷新UI。

若是要更新單個字段,能夠在覆蓋它以前檢查Realm文件中的值,以免觸發通知:

realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { String newName = "Jane"; Person managedPerson = getPerson(); if (!newName.equals(managedPerson.getName())) { managedPerson.setName(newName); } } });

若是要使用插入對象,Realm.copyToRealm()或者Realm.copyToRealmOrUpdate()可使用a ImportFlag來指示只應更新實際更改的字段:

realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { int id = 42; Person person = new Person(id, "Jane"); realm.copyToRealmOrUpdate(person, ImportFlag.CHECK_SAME_VALUES_BEFORE_SET); } });

這樣,只會針對實際更改的字段引起通知。使用此標誌時,null輸入對象中的值將被視爲正常值,所以若是Realm中的值爲非null,則將覆蓋該值null並觸發通知。

加密

請注意咱們許可證的出口合規部分,由於若是您位於有美國出口限制或禁運的國家/地區,它會對使用Realm進行限制。

經過將512位加密密鑰(64字節)傳遞給配置,能夠在磁盤上對Realm文件進行加密RealmConfiguration.Builder.encryptionKey

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

當給出加密密鑰時,Realm使用標準AES-256加密透明地加密和解密數據。每次打開Realm時都必須提供相同的加密密鑰。有關如何在Android KeyStore中的運行之間安全存儲密鑰以便其餘應用程序沒法讀取它們的示例,請參閱examples / encryptionExample

使用同步領域

您是否但願使用Realm Mobile Platform同步全部Realm數據庫?全部與同步相關的文檔已移至咱們的平臺文檔中

穿線

Realm能夠絕不費力地處理多個線程上的數據,而沒必要擔憂一致性或性能,由於對象查詢始終是自動更新的。您能夠對不一樣線程中的活動對象進行操做,讀取和寫入它們,而沒必要擔憂其餘線程正在對這些對象執行的操做。若是須要更改數據,可使用事務。另外一個線程中的其餘對象將近乎實時更新(更新將被安排爲事件上的事件Looper,所以Looper線程將在事件處理後當即更新)。

惟一的限制是您不能在線程之間隨機傳遞Realm對象。若是您須要在另外一個線程上使用相同的數據,則須要在另外一個線程上查詢該數據。此外,您可使用Realms反應體系結構觀察更改。請記住,全部對象在線程之間保持最新 - Realm會在數據更改時通知您

線程示例

假設咱們有一個顯示客戶列表的應用程序。在後臺線程(Android IntentService)中,咱們爲新客戶輪詢遠程端點,而後將它們保存到Realm。當後臺線程添加新客戶時,UI線程中的數據將自動更新。UI線程經過a得到通知RealmChangeListener,此時咱們告訴UI小部件自我更新。無需從新查詢,由於Realm使全部內容保持最新

// in a Fragment or Activity, etc // Listeners will only be triggered as long as the query result is // not garbage collected, so keep a strong class reference to it. private RealmResults<Customer> customers; @Override public void onActivityCreated(Bundle savedInstanceState) { // ... boilerplate omitted for brevity realm = Realm.getDefaultInstance(); // get all the customers customers = realm.where(Customer.class).findAllAsync(); // ... build a list adapter and set it to the ListView/RecyclerView/etc // set up a Realm change listener changeListener = new RealmChangeListener() { @Override public void onChange(RealmResults<Customer> results) { // This is called anytime the Realm database changes on any thread. // Please note, change listeners only work on Looper threads. // For non-looper threads, you manually have to use Realm.waitForChange() instead. updateUi(); } }; // Tell Realm to notify our listener when the customers results // have changed (items added, removed, updated, anything of the sort). customers.addChangeListener(changeListener); } // In a background service, in another thread public class PollingService extends IntentService { @Override public void onHandleIntent(Intent intent) { Realm realm = Realm.getDefaultInstance(); try { // go do some network calls/etc and get some data and stuff it into a 'json' var String json = customerApi.getCustomers(); realm.beginTransaction(); realm.createObjectFromJson(Customer.class, json); // Save a bunch of new Customer objects realm.commitTransaction(); // At this point, the data in the UI thread is already up to date. // ... } finally { realm.close(); } } // ... }

一旦後臺服務將新客戶添加到領域,該customers列表在UI中自動更新,而無需您進行任何其餘干預。這一樣適用於單個對象假設您只管理一個對象。只需在一個線程上更改它,UI線程就會自動擁有新數據。若是您須要響應該更改,只需像咱們上面所作的那樣添加一個監聽器

跨線程使用領域

跨線程使用Realm的惟一規則是記住Realm,RealmObject和RealmResults實例不能跨線程傳遞而是使用異步查詢異步事務將操做卸載到後臺線程,並將任何結果返回給原始線程。

當你想從不一樣的線程訪問相同的數據,你能夠獲得一個新的境界實例(即Realm.getInstance(RealmConfiguration config)它的表兄弟),並經過查詢獲取你的對象。

對象將映射到磁盤上的相同數據,而且能夠從任何線程讀取和寫入。

Android框架線程

使用這些類時要當心:

AsyncTask類包含doInBackground其執行後臺線程方法。IntentService類包含onHandleIntent(Intent intent)其執行工做線程的方法。

若是您須要在這兩種方法中使用Realm,您應該打開Realm,執行您的工做,而後在退出以前關閉Realm。如下是幾個例子。

的AsyncTask

doInBackground方法中打開和關閉Realm ,以下所示。

private class DownloadOrders extends AsyncTask<Void, Void, Long> { protected Long doInBackground(Void... voids) { // Now in a background thread. // Open the Realm Realm realm = Realm.getDefaultInstance(); try { // Work with Realm realm.createAllFromJson(Order.class, api.getNewOrders()); Order firstOrder = realm.where(Order.class).findFirst(); long orderId = firstOrder.getId(); // Id of order return orderId; } finally { realm.close(); } } protected void onPostExecute(Long orderId) { // Back on the Android mainThread // do something with orderId such as query Realm // for the order and perform some operation with it. } }

IntentService

ChangeListeners不適用於IntentService即便它是一個Looper線程,每次調用onHandleIntent都是一個不「循環」的獨立事件。這意味着能夠註冊更改偵聽器,但永遠不會觸發它們。

onHandleIntent方法中打開和關閉Realm ,以下所示。

public class OrdersIntentService extends IntentService { public OrdersIntentService(String name) { super("OrdersIntentService"); } @Override protected void onHandleIntent(Intent intent) { // Now in a background thread. // Open the Realm Realm realm = Realm.getDefaultInstance(); try { // Work with Realm realm.createAllFromJson(Order.class, api.getNewOrders()); Order firstOrder = realm.where(Order.class).findFirst(); long orderId = firstOrder.getId(); // Id of order } finally { realm.close(); } } }

多流程支持

能夠從多個進程訪問領域,但有一些限制當從同一個APK中的不一樣進程訪問同一個Realm時,包括通知在內的全部內容都應該正常工做。

例子

看看咱們的示例,看看Realm在應用程序中的實踐中使用。有關如何運行示例的更多詳細信息,請參見此處

introExample包含如何使用API王國簡單的例子。

gridViewExample是一個微不足道的應用程序,展現瞭如何使用領域做爲後備存儲的GridView控件。它還展現瞭如何使用GSON使用JSON填充數據庫以及如何使用ABI拆分來最小化最終APK的大小。

threadExample是一個簡單的應用程序,展現瞭如何在多線程環境中使用領域。

adapterExample展現瞭如何使用RealmBaseAdapterRealmRecyclerViewAdapter製做Realm與Android做業的ListViewRecyclerView以一種優雅的方式。

jsonExample演示領域的JSON設施。

encryptionExample展現瞭如何使用加密的國度工做。

rxJavaExamples顯示領域如何與RxJava在一塊兒。

unitTestExample顯示了域工做時如何編寫單元測試。

multiProcessExample展現瞭如何使用來自不一樣進程的域在同一個APK。

其餘圖書館

本節介紹如何將Realm與其餘經常使用的Android庫集成。

GSON

GSON是由Google建立的用於反序列化和序列化JSON的庫。GSON應與開箱即用的Realm合做。

// Using the User class public class User extends RealmObject { private String name; private String email; // getters and setters left out ... } Gson gson = new GsonBuilder().create(); String json = "{ name : 'John', email : 'john@corporation.com' }"; User user = gson.fromJson(json, User.class);

您還能夠在GridViewExample中看到GSON如何與Realm一塊兒使用的示例

序列化

爲了與Retrofit等庫徹底兼容,您一般但願可以反序列化和序列化對象。將Realm對象序列化爲JSON不適用於GSON的默認行爲,由於GSON將使用字段值而不是getter和setter

要使GSON序列化與Realm一塊兒使用,您須要爲每一個能夠序列化的對象編寫自定義JsonSerializer並將其註冊爲TypeAdapter

這個要點展現瞭如何作到這一點。

原始列表

雖然Realm支持將JSON中的數組本機導入到元列表中,但缺乏對基元列表的查詢支持可能仍然是一個問題。您可能但願將原始類型的JSON數組導入爲列表RealmObject若是沒法更改JSON API,則可覺得GSON 編寫自定義TypeAdapter,以自動映射JSON中的基元類型和Realm使用的包裝器對象。

在這個Gist中是一個使用Integers的包裝器對象的例子,可是該模板能夠用於Realm支持的數據類型的全部原始數組。

故障排除

Realm對象能夠包含內部包含循環引用的字段。當發生這種狀況時,GSON能夠拋出一個StackOverflowError咱們已經看到當Realm對象有一個Drawable字段時會發生這種狀況

public class Person extends RealmObject { @Ignore Drawable avatar; // other fields, etc }

Person上面類包含一個應用註釋的Android Drawable@Ignore在GSON序列化期間,正在檢查Drawable並致使StackOverflowError(GitHub問題)。要緩解此問題,請將如下代碼添加到您的shouldSkipField方法中。

public boolean shouldSkipField(FieldAttributes f) { return f.getDeclaringClass().equals(RealmObject.class) || f.getDeclaringClass().equals(Drawable.class); }

請注意Drawable.class評估。這告訴GSON在序列化期間跳過此字段。添加這將減輕StackOverflowError

傑克遜達比林德

Jackson Databind是一個用於將JSON數據綁定到Java類的庫。

傑克遜使用反射來執行數據綁定。這與Realm對RxJava的支持相沖突,由於RxJava可能沒法用於類加載器。這可能會致使異常,以下所示:

java.lang.NoClassDefFoundError: rx.Observable
at libcore.reflect.InternalNames.getClass(InternalNames.java:55)
...

這能夠經過將RxJava添加到項目中來修復,也能夠建立兩個以下所示的空虛擬文件。

// File 1 package io.reactivex; public class Flowable { } // File 2 package io.reactivex; public class Observable { } // File 3 package io.reactivex; enum BackpressureStrategy { LATEST; }

這個問題也在傑克遜項目中報告

科特林

Realm與Kotlin編程語言徹底兼容,但有一些須要注意的注意事項:

  • realm-android插件有後應用kotlin-androidkotlin-kapt這樣的:
apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'realm-android'
open class EnumTest: RealmObject() { // Beware that enums stored in Realm must not be obfuscated by ProGuard class enum MyEnum { Value1, Value2 } // Custom private backing field representing the enum private var strField: String = MyEnum.Value1.name // Public field exposing setting/getting the enum var enumField: MyEnum get() = MyEnum.values().first { it.name == strField } set(value) { strField = value.name } } // Queries val results = realm.where<EnumTest>().equalTo("strField", MyEnum.Value1.name).findAll();

請注意,查詢須要在strFieldwith String值上完成,而不是enumField由Realm忽略,由於它沒有後備字段

  • 在科特林Long::class.java實際上返回一個Class引用longLong這一樣適用於其餘原始類型像真正的IntegerFloatDoubleBoolean在遷移期間選擇正確的類會產生影響:
schema
  .addField("field", Long::class.java) // Non-nullable
  .addField("field", Long::class.javaObjectType) // Nullable
  .addField("field", Long::class.javaPrimitiveType) // Non-nullable

若是在項目中使用Kotlin,Realm會自動檢測到這一點並添加一些擴展方法,這使得與Kotlin的協做變得更加容易。其中包括:

  • 接受Java中的類參數的全部方法如今在Kotlin中都有一個具體化的變體,例如realm.where(Person.class).findAll()變爲realm.where<Person>().findAll()

  • 若是模型類實現了RealmModel接口,則如今會自動注入默認方法,這意味着不管是擴展基類RealmObject仍是實現接口,均可以使用徹底相同的調用模式RealmModel

  • 查詢謂詞in()如今有一個Kotlin別名,命名anyOf()Kotlin in中的關鍵字。

可使用realm閉包手動禁用此擴展庫

android { ... } realm { kotlinExtensionsEnabled = false // Disable extensions if needed }

有關Realm和Kotlin結合使用的應用程序,請參閱此示例

Kotlin 1.3.0有一個錯誤致使某些Kotlin代碼kapt失敗。這可能致使編譯時出現「未找到符號」等錯誤。目前惟一的解決方案是下降Kotlin的價格。這個問題正在跟蹤這裏

Parceler

Parceler是一個庫,它自動生成使對象尊重Parcelable接口所需的樣板因爲Realm使用代理類,Parceler須要如下設置才能使用Realm的模型類。

Realm中的代理類使用模型類的徹底限定名稱加上RealmProxy後綴,例如io.realm.model.Person變爲io_realm_model_PersonRealmProxy.class

// All classes that extend RealmObject will have a matching RealmProxy class created // by the annotation processor. Parceler must be made aware of this class. Note that // the class is not available until the project has been compiled at least once. @Parcel(implementations = { some_package_PersonRealmProxy.class }, value = Parcel.Serialization.BEAN, analyze = { Person.class }) public class Person extends RealmObject { // ... }

若是您使用Gradle獲取Parceler,請確保如下行(請參閱此處瞭解更多詳細信息):

compile "org.parceler:parceler-api:1.0.3" apt "org.parceler:parceler:1.0.3"

使用Parceler時須要注意一些重要的限制:

  1. 若是您的模型包含RealmList您須要註冊特殊適配器
  2. 一旦對象被分區,它就會與Realm分離,此時的行爲就像一個包含數據快照的非託管對象。Realm中不會持續對此對象進行進一步更改。

改造

RetrofitSquare的一個庫,它使得以類型安全的方式使用REST API變得容易。

Realm將同時使用Retrofit 1. *和2. *,但請注意Retrofit不會自動向Realm添加對象,而是必須使用realm.copyToRealmrealm.copyToRealmOrUpdate方法手動添加它們

GitHubService service = restAdapter.create(GitHubService.class); List<Repo> repos = service.listRepos("octocat"); // Copy elements from Retrofit to Realm to persist them. realm.beginTransaction(); List<Repo> realmRepos = realm.copyToRealmOrUpdate(repos); realm.commitTransaction();

Robolectric

Robolectric是一個庫,容許您直接在JVM中而不是在手機或模擬器中運行JUnit測試。目前,Robolectrics不支持與Realm捆綁在一塊兒的本機庫。這意味着目前沒法使用Robolectric測試Realm。

您能夠在此處按照功能請求進行操做:https//github.com/robolectric/robolectric/issues/1389

RxJava

RxJavaNetflix Reactive Extensions庫,它擴展了Observer模式它使得能夠將數據的變化視爲可組合序列。

Realm擁有對RxJava 2 FlowableRxJava 2的一流支持Observable

// Combining Realm, Retrofit and RxJava (Using Retrolambda syntax for brevity) // Load all persons and merge them with their latest stats from GitHub (if they have any) Realm realm = Realm.getDefaultInstance(); GitHubService api = retrofit.create(GitHubService.class); realm.where(Person.class).isNotNull("username").findAllAsync().asFlowable() .filter(persons.isLoaded) .flatMap(persons -> Observable.from(persons)) .flatMap(person -> api.user(person.getGithubUserName()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(user -> showUser(user));

異步查詢是非阻塞的 - 上面的代碼將當即返回一個RealmResults實例。若是您想確保只在加載列表上運行,請Flowable經過過濾器運算符過濾並經過調用[ RealmResults]檢查列表.isLoaded](api / io / realm / RealmResults.html#isLoaded--)方法。經過檢查是否加載了「RealmResults」,您能夠肯定您的查詢是否已完成。

有關更多示例,請參閱RxJava示例項目

RxJava是一個可選的依賴項,這意味着Realm不會自動包含它。這樣作的好處是,您能夠選擇使用哪一個版本的RxJava,以及避免不使用RxJava的項目中的方法計數膨脹。必須手動將RxJava添加到build.gradle文件中。

dependencies { compile 'io.reactivex:rxjava:2.1.4' }

能夠經過建立自定義來配置Realm如何建立流RxObservableFactory這是使用配置RealmConfiguration

RealmConfiguration config = new RealmConfiguration.Builder() .rxFactory(new MyRxFactory()) .build()

若是RxObservableFactory未定義,則Realm默認爲Realm RealmObservableFactory提供的類,它支持RxJava <= 2. *。

若是您使用的是RxJava 1,則可使用David Karnok的此庫在RxJava 2和RxJava 1類型之間進行轉換。

測試和調試

有關Realm如何與JUnit3,JUnit4,Robolectric,Mockito和PowerMock結合使用的信息,請參閱咱們的unitTestExample

Android Studio調試

在使用Android Studio或IntelliJ工做時須要注意一點「問題」:調試器可能會根據您使用的調試視圖提供誤導性值。

例如,在Android Studio中添加手錶RealmObject會顯示字段的值。不幸的是,這些值是錯誤的,由於不使用字段值。Realm在幕後建立一個代理對象,並覆蓋getter和setter以訪問Realm中的持久數據。爲任何訪問者添加監視將產生正確的值。見下圖:Android Studio,IntelliJ Debug RealmObject

在上圖中,調試器已在第113行中止。有三個監視值,即person變量person.getNameperson.getAge訪問器。第107到111行的代碼person經過更更名稱和年齡來改變實例。而後,這些值將保留在事務中。在第113行,調試器當前處於暫停狀態,person監視實例正在報告字段值,但它們不正確。使用訪問者的監視值person.getNameperson.getAge報告正確的值。

請注意,該.toString方法將輸出正確的值,可是監視面板不會(當觀察變量時是a RealmObject)。

NDK調試

Realm是一個包含本機代碼的庫。咱們建議您使用崩潰報告工具(例如Crashlytics)來跟蹤本機錯誤,以便在出現問題時咱們可以更好地幫助您。

調試NDK崩潰一般很麻煩,由於默認堆棧跟蹤提供了可用的最少信息。Crashlytics將容許您捕獲有價值的NDK崩潰信息。要在Crashlytics中啓用NDK崩潰報告,請按照本指南中列出步驟操做

要爲項目啓用NDK崩潰報告,請將其添加到build.gradle文件的根目錄。請注意,值androidNdkOutandroidNdkLibsOut是不須要的。

crashlytics { enableNdk true }

目前的侷限

Realm一般會嘗試儘量少的約束,而且咱們會根據社區的反饋不斷添加新功能。可是,Realm仍有一些侷限性。有關已知問題的更全面列表,請參閱咱們的GitHub問題。

楷模

領域模型不支持finalvolatile字段。這主要是爲了不對象在Realm或非託管方面的行爲方式之間存在差別。

不容許領域模型類擴展任何其餘對象RealmObject若是聲明,則默認構造函數(不帶參數的構造函數)必須始終爲空。緣由是默認的構造函數將調用假定存在Realm實例的方法。可是這個實例不是在構造函數返回以前建立的。爲方便起見,您能夠添加其餘構造函數。

通常

Realm旨在在靈活性和性能之間取得平衡。爲了實現這一目標,對在Realm中存儲信息的各個方面施加了現實限制。例如:

  1. 類名的上限爲57個字符。Realm Java class_以全部名稱爲前綴,瀏覽器將其顯示爲名稱的一部分。
  2. 字段名稱的長度上限爲63個字符。
  3. 在不一樣的包中不可能有兩個具備相同名稱的模型類。
  4. 不支持嵌套事務,若是檢測到異常,則拋出異常。
  5. Strings和字節數組(byte[])不能大於16 MB。
  6. 領域模型不支持finalvolatile字段。這主要是爲了不對象在Realm或非託管方面的行爲方式之間存在差別。
  7. 若是提供了自定義構造函數,則還必須存在公共無參數構造函數。
  8. Realm模型類不容許擴展任何其餘類RealmObject

對字符串進行排序和查詢

只有Latin Basic,Latin Supplement,Latin Extended A和Latin Extended B(UTF-8範圍0-591)中的字符集才支持查詢中的排序和不區分大小寫的字符串匹配。此外,使用時設置區分大小寫標誌查詢equalTonotEqualTocontainsendsWithbeginsWith,或like只從英語語言環境的字符工做。

Realm對大寫和小寫字母使用非標準排序,將它們排序在一塊兒而不是先排序大寫。這意味着它'- !"#0&()*,./:;?_+<=>123aAbBcC...xXyYzZ是Realm中的實際排序順序。瞭解更多關於這些限制在這裏

主題

儘管Realm文件能夠由多個線程同時訪問,但您沒法在線程之間移交Realms,Realm對象,查詢和結果。線程示例演示如何在多線程環境中使用Realm。閱讀有關Realm線程的更多信息。

儘管Realm文件能夠由多個線程同時訪問,但它們一次只能由一個進程訪問。不一樣的進程應該複製Realm文件或建立本身的文件。

RealmObject的hashCode

RealmObject是活動對象,可能會經過其餘線程的更改進行更新。雖然回國2個境界對象trueRealmObject.equals必須有相同的值RealmObject.hashCode,這個值是否是穩定,既不該該被用做一個鍵HashMap,也沒有保存在HashSet

多進程

  • 不支持同時訪問不一樣進程的加密域。有一個Realm Core問題(#1845)跟蹤此問題。
  • 不支持從不一樣APK中的不一樣進程訪問相同的域。這樣作是安全的,但通知等內容沒法按預期工做。
  • 不支持從不一樣進程訪問同步的域。

增量構建

領域字節碼轉換器支持增量構建,但在少數狀況下須要完整構建。變壓器自己沒法檢測到這些狀況。

  • @Ignore在模型類的字段中添加或刪除註釋。
  • static在模型類的字段中添加或刪除關鍵字。
  • transient在模型類的字段中添加或刪除關鍵字。

在這些狀況下未能執行完整構建將致使應用程序崩潰或致使字段返回數據類型的默認值(例如0或null),而不是返回存儲在Realm中的真值。

最佳作法

開箱即用,Realm能夠與Android無縫協做。你必須記住的主要事情是RealmObjects是線程限制的當您想要在活動,後臺服務,廣播接收器等之間開始傳遞Realm對象時,理解這一點的重要性就會發揮做用。

防止「應用程序無響應」(ANR)錯誤

一般,Realm足夠快,能夠在Android主線程上讀寫數據。可是,寫入事務在線程之間是阻塞的,所以爲了防止意外的ANR,咱們建議您在後臺線程(而不是Android的主線程)上執行全部Realm寫操做。瞭解如何經過Realms Asynchronous Transactions在後臺線程上執行操做

控制Realm實例的生命週期

爲Realm實例選擇合適的生命週期是一種平衡行爲。由於RealmObjects而且RealmResults經過惰性緩存訪問,因此儘量長時間地保持Realm實例打開不只能夠避免打開和關閉它所產生的開銷,並且可能容許對它的查詢更快地運行。另外一方面,開放的Realm實例擁有大量資源,其中一些資源不受Java內存管理器控制。Java沒法自動管理這些資源。打開Realm實例的代碼必須在再也不須要時關閉它。

Realm使用內部引用計數緩存,以便在獲取第一個Realm實例後,在同一個線程上獲取後續實例是免費的。可是,只有當該線程上的全部實例都關閉時,纔會釋放底層資源。

一個合理的選擇是使Realm實例的生命週期與觀察它的視圖的生命週期一致。下面的示例演示了使用a Fragment和an Activity,每一個示例RecyclerView顯示從Realm實例檢索的數據。在這兩個示例中,Realm實例和RecyclerView適配器在create方法中初始化,並在相應的destroy方法中關閉。請注意,即便對於Activity,這也是安全的:即便從未調用onDestroyclose方法,數據庫也將保持一致狀態

顯然,若是與活動相關聯的大多數片斷須要訪問同一數據集,那麼控制實例生命週期的活動(而不是單個片斷)是有意義的。

// Setup Realm in your Application public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); Realm.init(this); RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().build(); Realm.setDefaultConfiguration(realmConfiguration); } } // onCreate()/onDestroy() overlap when switching between activities. // Activity2.onCreate() will be called before Activity1.onDestroy() // so the call to getDefaultInstance in Activity2 will be fast. public class MyActivity extends Activity { private Realm realm; private RecyclerView recyclerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); realm = Realm.getDefaultInstance(); setContentView(R.layout.activity_main); recyclerView = (RecyclerView) findViewById(R.id.recycler_view); recyclerView.setAdapter( new MyRecyclerViewAdapter(this, realm.where(MyModel.class).findAllAsync())); // ... } @Override protected void onDestroy() { super.onDestroy(); realm.close(); } } // Use onCreateView()/onDestroyView() for Fragments. // Note that if the db is large, getting the Realm instance may, briefly, block rendering. // In that case it may be preferable to manage the Realm instance and RecyclerView from // onStart/onStop instead. Returning a view, immediately, from onCreateView allows the // fragment frame to be rendered while the instance is initialized and the view loaded. public class MyFragment extends Fragment { private Realm realm; private RecyclerView recyclerView; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { realm = Realm.getDefaultInstance(); View root = inflater.inflate(R.layout.fragment_view, container, false); recyclerView = (RecyclerView) root.findViewById(R.id.recycler_view); recyclerView.setAdapter( new MyRecyclerViewAdapter(getActivity(), realm.where(MyModel.class).findAllAsync())); // ... return root; } @Override public void onDestroyView() { super.onDestroyView(); realm.close(); } }

重用RealmResults和RealmObjects

在UI線程和全部其餘Looper線程上,當對Realm進行更改時,全部RealmObjects和都會RealmResults自動刷新。這意味着在對a做出反應時沒必要再次獲取這些對象RealmChangedListener對象已更新,能夠在屏幕上從新繪製。

public class MyActivity extends Activity { private Realm realm; private RealmResults<Person> allPersons; private RealmChangeListener realmListener = new RealmChangeListener() { @Override public void onChange(Realm realm) { // Just redraw the views. `allPersons` already contain the // latest data. invalidateView(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); realm = Realm.getDefaultInstance(); realm.addRealmChangeListener(listener); allPerson = realm.where(Person.class).findAll(); // Create the "live" query result setupViews(); // Initial setup of views invalidateView(); // Redraw views with data } // ... }

自動增量ID

Realm不支持自動增量ID。這主要是由於不可能在分佈式環境中生成這樣的密鑰,而且存儲在本地Realm和同步Realm中的數據之間的兼容性是高優先級。須要注意的是境界並不須要爲了建立關係的主鍵。

它仍然是能夠有效地建立境界是知足自增的ID提供的用例的主鍵,而是肯定是很重要的東西的自增ID用於:

1)提供惟一標識符以識別對象。這能夠用GUID代替,它能夠保證惟一性,即便在設備離線時也能夠由設備建立:

```java
public class Person extends RealmObject {
    @PrimaryKey
    private String id = UUID.randomUUID().toString();
    private String name;
}
```

2)提供寬鬆的下單一個例子是排序推文。這能夠由一個createdAt字段替換,該字段不須要是主鍵:

```java
public class Person extends RealmObject {
    @PrimaryKey
    private String id = UUID.randomUUID().toString();
    private Date createdAt = new Date();
    private String name;
}
```

3)提供嚴格的下單一個例子是任務列表。RealmList即便設備已脫機,也可使用保證插入順序的方式對其進行建模

```java
public class SortedPeople extends RealmObject {
    @PrimaryKey
    private int id = 0
    private RealmList<Person> persons;
}

public class Person extends RealmObject {
    private String name;
}

// Create wrapper object when creating object
RealmConfiguration config = new RealmConfiguration.Builder()
.initialData(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        realm.insert(new SortedPeople());
    }
});

// Insert objects through the wrapper
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        SortedPeople sortedPeople = realm.where(SortedPeople.class).findFirst();
        sortedPeople.getPersons().add(new Person());
    }
});
```

若是您有一個用例仍然認爲自動增量ID更適合,您能夠使用此助手類,但請注意,若是您:使用此類生成的密鑰不可用:

1)在多個進程中建立領域對象。2)但願在未來的某個時刻在多個設備之間共享領域。

對於自動增量跨進程可安全建立的ID,每次開始事務時都須要查詢最大值:

realm.beginTransaction(); Number maxValue = realm.where(MyObject.class).max("primaryKeyField"); long pk = (maxValue != null) ? maxValue + 1 : 0; realm.createObject(MyObject.class, pk++); realm.createObject(MyObject.class, pk++); realm.commitTransaction();

食譜

咱們已經彙總了一些顯示如何使用Realm來完成一些特定任務的方法。咱們會按期添加更多食譜,所以請常常查看。若是您想看一個例子,請在GitHub上打開一個問題

常問問題

如何查找和查看個人Realm文件的內容?

這個SO問題描述了在哪裏找到您的Realm文件。而後,您可使用咱們的Realm Studio查看內容

Realm Base庫有多大?

一旦您的應用程序構建爲發佈並拆分以進行分發,Realm在大多數狀況下應僅向您的APK添加大約800KB。咱們分發的版本要大得多,由於它們包括對更多架構(ARM7,ARMv7,ARM64,x86,MIPS)的支持。APK文件包含全部支持的體系結構,但Android安裝程序將僅爲設備的體系結構安裝本機代碼。所以,安裝的應用程序小於APK文件的大小。

經過將APK拆分爲每一個架構的版本,能夠減少Android APK自己的大小。使用Android Build Tool ABI Split支持,將如下內容添加到build.gradle

android { splits { abi { enable true reset() include 'armeabi-v7a', 'arm64-v8a', 'mips', 'x86', 'x86_64' } } }

選擇您要包含的體系結構,併爲每一個體繫結構構建單獨的APK。詳細信息,有關ABI Splits Android工具文檔

一個例子也包含在GitHub中

若是您不想處理多個APK,也能夠限制單個APK中支持的體系結構數量。這是經過添加abiFilters到您的build.gradle

android { defaultConfig { ndk { abiFilters 'armeabi-v7a', 'arm64-v8a', 'mips', 'x86', 'x86_64' } } }

有關ABI拆分和過濾器的更多詳細信息,請閱讀本文 Brijesh Masrani撰寫的

Realm開源嗎?

是! Realm的內部C ++存儲引擎及其上的語言SDK徹底是開源的,並在Apache 2.0下得到許可。Realm還可選擇包含一個閉源Realm Platform Extensions組件,但不須要將Realm用做嵌入式數據庫。

普通Java對象和Realm對象有什麼區別?

主要區別在於普通Java對象包含本身的數據,而Realm對象不包含數據,而是直接在數據庫中獲取或設置屬性。

Realm對象的實例能夠是託管實例,也能夠是非託管實例。

  • 託管對象在Realm中持久存在,始終是最新的而且線程受限。它們一般比非託管版本更輕量級,由於它們在Java堆上佔用的空間更少。
  • 非託管對象就像普通的Java對象同樣,它們不會被持久化,也不會自動更新。它們能夠跨線程自由移動。

可使用Realm.copyToRealm在兩種狀態之間進行轉換Realm.copyFromRealm

爲何模型類須要擴展RealmObject?

咱們須要爲您的模型類添加Realm特定功能。它還容許咱們在API中使用泛型,使其更易於閱讀和使用。若是您不想擴展基類,則能夠改成實現RealmModel接口。

什麼是RealmProxy類?

RealmProxy類是咱們確保Realm對象自己不包含任何數據,而是直接在數據庫中訪問數據的方法。

對於項目中的每一個模型類,Realm註釋處理器將生成相應的RealmProxy類。此類擴展了模型類,而且是在調用Realm.createObject()時返回的內容,但從代碼的角度來看,您不會注意到任何差別。

爲何在編寫Realm對象時須要使用事務?

須要事務來確保將多個字段更新爲一個原子操做。它容許您定義必須徹底完成或根本不完成的更新範圍(若是出現錯誤或受控回滾)。經過指定事務的範圍,您能夠控制更新的持續頻率(或快速)(即在一次操做中插入多個對象)。

在像SQLite這樣的普通基於SQL的數據庫中進行插入時,一次插入多個字段。這會自動包裝在事務中,但一般對用戶不可見。在Realm中,這些事務始終是明確的。

如何處理內存不足異常?

Realm for Android基於嵌入式存儲引擎。存儲引擎不在JVM堆上分配內存,而是在本機內存中分配內存。當存儲引擎沒法分配本機內存或文件系統已滿時,Realm將拋出java.lang.OutOfMemoryError異常。重要的是不要忽略此錯誤。若是您的應用程序繼續運行,則訪問Realm文件可能會使其處於已損壞或不一致的狀態。最安全的是終止應用程序。

大領域文件大小

您應該指望Realm數據庫在磁盤上佔用的空間少於等效的SQLite數據庫,但爲了給您一致的數據視圖,Realm能夠在Realm的多個版本上運行。若是最舊版本和最新版本的數據之間的差別變得太大,這可能會致使Realm文件不成比例地增加。

若是再也不使用它們,Realm將自動刪除舊版本的數據,但實際文件大小不會減小。將來的寫入將重用額外的空間。

若是須要,能夠經過壓縮Realm文件來刪除額外的空間。這能夠手動自動完成在第一次打開所述境界時。

若是您遇到意外的文件大小增加,一般會出現如下兩個緣由之一:

1)你在後臺線程上打開一個領域,忘記再次關閉它。

這將致使Realm保留對後臺線程上的數據的引用,而且是致使Realm文件大小問題的最多見緣由。解決方案是確保正確關閉您的Realm實例。在這裏這裏閱讀更多Realm將檢測您是否忘記正確關閉Realm實例並在Logcat中打印警告。帶有loopers的線程(如UI線程)沒有此問題。

2)您從Realm讀取一些數據,而後在長時間運行的操做中阻塞該線程,同時在其餘線程上向Realm寫入屢次。

這將致使Realm建立許多須要跟蹤的中間版本。避免這種狀況有點棘手,但一般能夠經過批處理寫入或避免讓Realm打開而以其餘方式阻止後臺線程來完成。

我在運行應用程序時看到了對Mixpanel的網絡調用

當您在源代碼上運行Realm字節碼轉換器時,Realm會收集匿名分析。這是徹底匿名的,能夠經過標記您使用的Realm版本以及您使用的操做系統以及咱們能夠棄用支持的內容來幫助咱們改進產品。當您的應用程序在用戶的設備上運行時,此調用不會運行 - 僅在編譯源代碼時纔會運行您能夠在咱們的源代碼中查看咱們收集的具體方式和內容,以及它的基本原理

沒法加載「librealm-jni.so」

若是您的應用程序使用的其餘本​​機庫不支持64位體系結構,則Android將沒法librealm-jni.so在ARM64設備上加載Realm的文件。這是由於Android沒法同時加載32位和64位本機庫。最好的解決方案是讓全部庫提供相同的受支持的ABI集,但有時若是您使用第三方庫則可能沒法實現。請參閱VLC和Realm Library衝突

此問題的解決方法是經過將如下代碼添加到應用程序來從APK文件中排除Realm的ARM64庫build.gradle您能夠參考Android中的混合32位和64位依賴項以獲取更多信息。

android { //... packagingOptions { exclude "lib/arm64-v8a/librealm-jni.so" } //... }

此外,Android Gradle Plugin 1.4.0測試版存在一個錯誤,致使它不正確地打包jar文件中包含的.so文件(參見Realm Java issue 1421))。要解決此問題,您能夠恢復到Android Gradle Plugin 1.3.0或使用Android Gradle Plugin 1.5.0+。

咱們知道許多第三方庫,框架和管理應用程序尚未64位支持:

如何備份和恢復領域?

領域存儲在文件系統上的文件中。經過調用getPath您能夠得到Realm文件的完整路徑。若是您打算以這種方式備份或還原Realm文件,則應關閉Realm的全部實例。

也可使用realm.writeCopyTo備份打開的Realm文件

若是您要將文件備份到Google雲端硬盤等外部位置。您能夠閱讀本教程:第1 部分第2 部分第3部分

黑莓設備

一些Blackberry設備可以運行Android應用程序。遺憾的是,提供的運行時環境不完整,咱們沒法保證兼容性。已知的錯誤消息包括:

io.realm.exceptions.RealmFileException: Function not implemented in io_realm_internal_SharedRealm.cpp line 81 Kind: ACCESS_ERROR.

若是您發現Blackberry設備存在問題,請考慮提供修復,由於Realm Core和Realm Java都是開源項目。

如何存儲和檢索Realm使用的加密密鑰

使用Android KeyStore多是存儲Realm加密密鑰的最安全方式。如下是推薦使用它的方法。

  1. 使用Android的KeyStore,生成一個非對稱RSA密鑰,由Android安全存儲/檢索。在版本> = M系統須要用戶PIN(或指紋)來解鎖KeyStore,所以即便在有根設備上,您也有額外的安全層。
  2. 生成對稱密鑰(AES),用於加密Realm。
  3. 使用私有RSA密鑰加密對稱AES密鑰。
  4. 如今將加密的AES密鑰存儲在文件系統上安全的SharedPreferences例如)。
  5. 當您須要使用加密的Realm時,檢索加密的AES密鑰,使用公共RSA密鑰對其進行解密,而後在其中使用它RealmConfiguration來打開加密的Realm。

有關端到端的示例,請查看咱們的演示存儲庫:

如何在自定義ROM上的系統應用程序中使用Realm

Realm使用命名管道來支持通知和從多個進程訪問Realm文件。雖然普通用戶應用程序默認容許這樣作,但系統應用程序不容許這樣作。

系統應用程序是經過設置android:sharedUserId="android.uid.system"Android清單中的設置來定義的,若是您正在建立這樣的應用程序,您可能會在Logcat中看到相似這樣的安全違規:

05-24 14:08:08.984  6921  6921 W .realmsystemapp: type=1400 audit(0.0:99): avc: denied { write } for name="realm.testapp.com.realmsystemapp-Bfqpnjj4mUvxWtfMcOXBCA==" dev="vdc" ino=14660 scontext=u:r:system_app:s0 tcontext=u:object_r:apk_data_file:s0 tclass=dir permissive=0
05-24 14:08:08.984  6921  6921 W .realmsystemapp: type=1400 audit(0.0:100): avc: denied { write } for name="realm.testapp.com.realmsystemapp-Bfqpnjj4mUvxWtfMcOXBCA==" dev="vdc" ino=14660 scontext=u:r:system_app:s0 tcontext=u:object_r:apk_data_file:s0 tclass=dir permissive=0

爲了解決這個問題,您須要調整ROM中的SELinux安全規則。這能夠經過使用工具來完成,該工具audit2allow是做爲AOSP的一部分提供的工具。

1)首先從設備中提取當前策略adb pull /sys/fs/selinux/policy2)將SELinux錯誤複製到名爲的文本文件中input.txt3)運行audit2allow工具:audit2allow -p policy -i input.txt4)該工具應輸出您能夠添加到現有策略中的規則,以容許您使用Realm。

下面提供了有關此類策略的外觀的示例:

# Allow system_app to create named pipes required by Realm
# Credit: https://github.com/mikalackis/platform_vendor_ariel/blob/master_oreo/sepolicy/system_app.te
allow system_app fuse:fifo_file create;
allow system_app system_app_data_file:fifo_file create;
allow system_app system_app_data_file:fifo_file { read write };
allow system_app system_app_data_file:fifo_file open;

audit2allow在編譯AOSP / ROM時生成而且僅在Linux上運行。你能夠在這裏閱讀更多相關信息另請注意,自Android Oreo以來,Google改變了配置SELinux的方式,如今默認的安全策略更加模塊化。在這裏閱讀更多相關信息

如何自定義Realm Gradle插件定義的依賴項?

Realm使用Gradle插件,由於它能夠更容易地設置大量的依賴項,但遺憾的是它也使得自定義更難,例如,若是你想忽略一些傳遞依賴。

若是您但願自定義Realm超出插件公開的範圍,您能夠手動設置全部依賴項並忽略Gradle插件。如何爲Kotlin項目執行此操做以下所示:

使用gradle插件時的標準方法:

buildscript { ext.kotlin_version = '1.2.41' repositories { jcenter() mavenCentral() } dependencies { classpath "io.realm:realm-gradle-plugin:5.12.0" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'realm-android'

手動設置:

buildscript { ext.kotlin_version = '1.2.41' ext.realm_version = '5.12.0' repositories { jcenter() mavenCentral() } dependencies { classpath "io.realm:realm-transformer:$realm_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' import io.realm.transformer.RealmTransformer android.registerTransform(new RealmTransformer(project)) dependencies { api "io.realm:realm-annotations:$realm_version" api "io.realm:realm-android-library:$realm_version" api "io.realm:realm-android-kotlin-extensions:$realm_version" kapt "io.realm:realm-annotations-processor:$realm_version" }

若是您正在使用Realm對象服務器,realm-android-kotlin-extensions而且realm-android-library須要加上後綴,-object-server那麼它們將成爲:realm-android-kotlin-extensions-object-serverrealm-android-library-object-server

如何從虛擬機調試?

Chrome調試器嘗試鏈接到端口8083上運行的域服務器。

您必須將8083端口發送到主機的8083端口。

socat tcp-listen:8083,bind=localhost,reuseaddr,fork tcp:<VM address>.1:8083

而後,您須要重定向 到`localhost:8083`。

socat tcp-listen:8083,bind=<VM address>,reuseaddr,fork tcp:localhost:8083

爲了到達Android端口,您可能須要重定向localhost:8083到android的端口。若是您運行npm run android自動運行,則會發生這種狀況adb forward tcp:8083 tcp:8083這將使得localhost:8083到達運行Realm服務器的android的8083端口。

相關文章
相關標籤/搜索