课程咨询 :186 8716 1620      qq:2066486918

昆明Java培训 > 达内新闻 > Realm Java原理介绍以及常见问题
  • Realm Java原理介绍以及常见问题

    发布:陈牧龙      来源:达内新闻      时间:2016-11-16

  • 昆明Java培训班的老师知道,MVCC解决了一个重要的并发问题:在所有的数据库中都有这样的时候,当有人正在写数据库的时候有人又想读取数据库了(例如,不同的线程可以同时读取或者写入同一个数据库)。这会导致数据的不 一致性-可能当你读取记录的时候一个写操作才部分结束。如果数据库允许这种事情发生,你就会得到和最终数据库里的数据不一致的数据。

    有很多的办法可以解决读、写并发的问题,最常见的就是给数据库加锁。在之前的情况下,我们在写数据的时候就会加上一个锁。在写操作完成之前,所有的读操作都会被阻塞。这就是众所周知的读-写锁。这常常都会很慢。

    类似Realm的MVCC的数据库采用了另外的一个方法:每一个连接的线程都会有数据在一个特定时刻的快照。

    相关厂商内容

    跟技术大牛,侃侃容器那些事儿!关于红包、SSD云盘等核心技术集锦!看明略任鑫琦如何谈关系挖掘算法看明略徐安华如何谈自然语言处理中国技术开放日上海站:FinTech-技术重定金融,未来大有不同(免费报名)在线程2写入 过程中,线程1的读取操作并不会被阻塞,其仍然能够正常访问数据库版本V1的所有节点。

    Realm的懒加载大部分的时候,你都把数据存在磁盘上的数据库文件中。开发者发起一个从持久化机制(比如ORM或者Core Data)中获取数据的请求,数据格式会是和本地平台密切相关的(比如安卓或者苹果)。这个时候,持久化机制 会把请求转换成一系列的SQL语句,创建一个数据库连接(如果没有创建的话),发送到磁盘上,执行查询,读取命中查询的每一行的数据,然后存到内存里(这里有内存消耗)。之后你需要把数据序列化成可在内存里面存储的格 式,这意味着比特对齐,这样CPU才能处理它们。

    最后,数据需要转换成语言层面的类型,然后它会以对象的形式返回,这样平台才能用(POJO,NSManagedObject等等)来处理它。如果你在你的持续化机制中有子引用或者列表引用的话,这个过程会更复杂。这个过程会一遍一遍的执 (取决于你的持续化机制和配置)。如果你使用自产自销的机制,情况也大致相同。

    Realm的方法不一样。这就是我们零拷贝架构起作用的地方。

    Realm跳过了整个拷贝过程,因为数据库文件是memory-mapped。Realm在访问文件偏移的时候就好像文件已经在内存中一样,实际上不是,而是虚拟内存。这是个Realm核心文件格式的重要设计决定。它允许文件能在没有做任何反序列化的 况下可以在内存中读取。

    Realm跳过了所有这些开销很大的步骤,而这些步骤在传统的持久化机制中必须执行。Realm只需要简单地计算偏移来找到文件中的数据,然后从原始访问点返回数据结构(POJO/NSManagedObject/等等)的值。这更有效而且更快。

    Realm Java介绍上文中所提到的Realm与MVCC相关的概念在所有的Realm产品中都适用,接下来我们介绍一下在Realm Java中这些概念是怎么与Java语言和安卓框架相结合并实现的。

    线程在Realm Java中你可以使用Realm.getInstance()(或者Realm.getDefaultInstance())来在当前线程中获得一个Realm实例。Realm使用引用计数管理每个线程中的Realm实例。多次针对同一个RealmConfiguration在同一线程中调用会返回同一个Realm实例。Re alm实现了Closeable接口,这意味这每一次的getInstance()调用都应该对应一个close()调用以释放相应的资源。

    如果getInstance()是第一次在当前线程调用,那么它会在当前最新的数据版本之上打开一个新的Realm实例。

    对于一个拥有安卓Looper的线程,Realm通过安卓的Handler系统来通知各个线程中的Realm实例有写入操作发生。举例来说,假设线程1是安卓UI线程,当线程2中对Realm进行了写入操作后,线程1的Realm会在下一次Looper事件中更新到线程2写 后的数据版本。

    对于一个非Looper线程来说,Realm的数据版本更新依赖于Realm.waitForChange()调用。该调用会阻塞当前线程直到其他线程有写入操作完成。

    Realm对象代理和字节码替换Realm通过使用注解处理和字节码变换来联系RealmObject和Realm数据存储。我们通过下面这个简单的例子来了解一下这个过程。

    首先,我们注意到了有四个与DogRealmProxy类一一对应新的方法(realmGet$xxx/realmSet$xxx)被插入到了Dog类中,这四个新方法只是简单的setter和getter;其次,在函数getAge()、setAget()以及printName中所有对Dog属性的直接访问都被替换成了相 生成的方法realmGet$xxx和realmSet$xxx。

    这就是全部的秘密所在了。在从Realm实例中获取任何Realm对象的时候(比如调用Realm.createObject()或者RealmQuery.findFirst()),你实际上是获取了这个对象相应的Realm代理对象。对其属性的访问实际上都是通过相应生成的方法来访问底 的Realm数据库来实现的。

    同时这也解释了我们之前提到的Realm的懒加载特性。在查询返回一个或者多个Realm对象的时候,这些对象的属性并没有被拷贝到Java堆中,这使得Realm的查询非常得快。这些属性只在需要被访问的时候,才经由生成的getter方法加载

    Realm Java常见问题在了解了Realm的这些关键实现之后,如下这些常见问题也就不难解释了。

    跨线程Realm访问Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.

    这是一个初次使用Realm时常见的异常。请注意,RealmObject、RealmResults等相关对象都是与其线程中的Realm实例绑定的。因为两个线程中的Realm实例可能锁定了不同的Realm版本,这些对象也可能处于不同的数据版本,跨线程访问会引起 据的不一致性。所以,在另一个线程中访问同一个对象的时候,请在该线程中进行查询以获得这个对象绑定该线程Realm的实例。或者使用Realm提供的相应的异步查询接口,具体请参考相关文档。

    托管Realm对象与非托管Realm对象在Realm文档里这两个概念(managed Realm object/unmanaged Realm Object)尝尝被提及。通过以上的介绍,我们不难想象这里的托管Realm对象(managed Realm object)指的是Realm的代理对象实例,例如DogRealmProxy的实例;而非托管Realm对象指的是(unmanaged Relam object)原始对象的实例,例如Dog的实例。

    我们也不难想象,对于非托管Realm对象来说,他可以经由类似new Dog()的方式创建,而且对它本身属性的访问并不会引起任何对Realm数据库的访问。

    当然,非托管Realm对象仍然可以被保存到Realm数据库中并且相应地返回一个托管Realm对象。

    另外,显而易见,非托管Realm对象不具备Realm托管对象的一切高级特性,比如自动更新特性。

    这里声明的AsyncTask会在每次执行的时候打开一个Realm实例,但是并没有在使用结束后关闭。通过我们对Realm线程相关的介绍,不难想象这会导致某一Realm数据版本被该线程中的Realm实例锁定,因为Realm实例没有被正确关闭,Realm无 得知其对应的数据已经不需要再被访问。假设这个AsyncTask在后台被反复执行,同时又有另一个线程在不断更新着Realm的数据,那么每一个AyncTask都会锁定一个不同的Realm数据版本,从而导致Realm文件的体积不断变化。所以,请在后 台线程结束时关闭相应的Realm实例。

    Realm库与apk大小Realm几乎发布了针对所有ABI的so文件。如果你的应用在google play市场发布,那么你可以很方便的通过google官方提供的apk Split将各种ABI分开打包。但假设你的应用是在国内市场发布,ABI Split可能无法正常工作,你可以 考虑只包含部分so文件(例如arm64设备兼容armeabi和armeabi-v7a,而只支持armeabi的设备几乎没有人使用了)。具体信息可以查看Realm的文档。

    推荐文章

上一篇:RxJava 2.0.1发布,Rx的Java实现

下一篇:Java 5以前的并发编程

最新开班日期  |  更多

Java--零基础全日制班

Java--零基础全日制班

开班日期:11/30

Java--零基础业余班

Java--零基础业余班

开班日期:11/30

Java--周末提升班

Java--周末提升班

开班日期:11/30

Java--零基础周末班

Java--零基础周末班

开班日期:11/30

  • 网址:http://km .java.tedu.cn      地址:昆明市官渡区春城路62号证券大厦附楼6楼
  • 课程培训电话:186 8716 1620      qq:2066486918    全国服务监督电话:400-827-0010
  • 服务邮箱 ts@tedu.cn
  • 2001-2016 达内国际公司(TARENA INTERNATIONAL,INC.) 版权所有 京ICP证08000853号-56