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端口。

相关文章
相关标签/搜索