架构师之路一席AndroidiOS 技术文档

为什么我决定使用Realm数据库

2016-11-26  本文已影响1478人  欧阳锋

在移动领域,SQLite数据库一直占据着不可撼动的地位。SQLite的缔造者遵循小而精的理念,使用极少量的C代码实现了关系型数据库的核心逻辑。然而,当关系型数据库遇到了面向对象编程语言时,一个困扰着程序员们的难题出现了。为了实现关系型数据到对象的转换,我们需要使用SQL语句查询到相应的值。然后,再给对象赋值。查询最典型实现就是Cursor,在使用完Cursor后还必须手动关闭。这些重复的工作给编码带来了一些麻烦,为了解决这个难题,ORM框架出现了...

ORM (Object Relational Mapping)

在Java Web开发中,比较经典ORM实现是Hibernate。在几乎成为了Java Web开发中的标配,一些培训机构有专门的课程对Hibernate的使用进行讲解,在面试的时候,也会对Hibernate的知识进行考核。由此可见,Hibernate在Java Web开发中的影响力。

那么,ORM框架到底做了什么呢?

其实很简单,ORM框架避免了你直接使用SQL语句进行查询。而是直接使用面向对象的方式帮助你完成数据的查询,并且自动映射到相应的对象中。这对于一些受够了繁琐SQL语句编写的程序员来说,简直就是救星。

所有的事务都具有两面性,ORM带来了极大便利性的同时,也带来了一些性能损耗;同时,由于Hibernate内部实现的复杂性,也对程序员对于Hibernate使用的优化提高了要求。在ORM使用的抉择中,性能问题是很多程序员考量的标准。其实,在Web开发的大多数情况下,Hibernate引起的性能损耗都是可以接受的(一些脚本语言,例如PHP,性能更低,却依然被广泛用于Web开发),多数情况下可以考虑增加服务器匹配性能损耗。

然而,在移动开发中,这个问题却让我们不得不重新思考。在iOS平台上,iOS5版本以后苹果工程师新增了一个SQLite的ORM框架CoreData,这几乎成为了iOS程序员必选的数据库解决方案。而在Android平台上,谷歌官方却并没有提供相应的ORM框架,这应该也是因为谷歌工程师对于Android平台性能的综合考量,决定放弃ORM框架的使用。

可问题是,为什么iOS平台可以,Android却不行?

答案其实很简单,Android app是运行在Davilk虚拟机之上的,其性能无法和iOS app相提并论。因此,在Android应用开发中,如何提升Android应用性能成为了考量一个Android程序员是否具备专业水准的重要指标。

在一开始的抉择中,为了提升app性能,我没有选择使用ORM框架。可是,在对SQLite使用进行封装的时候,我的纠结症犯了。

沿袭Web开发的技术,对于数据库的使用,有两种比较典型的封装,一种是DAO,即:Data Access Object, 这个设计的逻辑是,应用层不直接访问数据库,而是将数据库的访问交给DAO层。由DAO完成对所有数据的访问。

下面来看一看常见的设计类图:


这种封装方式看起来似乎很有道理,可仔细分析,它并不完美,尤其在对数据库比较依赖的应用中,将产生大量的DAO类。而且,在对数据的提取过程中,依然需要做大量的工作。在应用越来越庞大的时候,针对数据库部分的维护也会带来一些麻烦。所以,笔者认为,It's not perfect 。

针对数据库的封装,还有一种比较典型的使用方式。那就是Java设计模式之一:Repository, Repository和DAO设计有异曲同工之处。关于Repository设计模式的介绍,建议参考这篇文章 Repository设计模式
可以看到,It still not perfect 。

关于数据库的封装,我无法满足于DAORepository的封装方式,这的确会让代码看起来Very ugly。放弃了这种解决方案之后,我开始尝试基于SQLite的ORM解决方案。

基于SQLite的ORM解决方案的确是五花八门,最常见的有如下几种:

以上几种解决方案中,除GreenDAO之外,大都使用反射和注解的方式动态进行处理,这种处理方式是比较消耗性能的,在网友的增删改查实验对比中,基本都落后于GreenDAO,这让我优先选择GreenDAO作为解决方案。然后,在看了一些介绍文档之后,我最终决定放弃GreenDAO的使用。

使用GreenDAO,依然需要很高的成本。
1) 使用一个单独的Java工程动态生成代码
2) 创建相应的类实现代码生成逻辑。
...

仅仅第一条便让我放弃了对GreenDAO的使用,在继续寻找解决方案的过程中,我知道了今天文章的主角-Realm

那么,Realm到底又是何方神圣呢?

其实,Realm依然是关系型数据库的ORM解决方案,比较不可思议的是:Realm在增删改查操作中速度几乎都优于SQLite,或者接近SQLite。同样是ORM框架,为什么Realm可以拥有如此惊人的速度呢?

答案是:Realm的底层实现基本采用C++, 原生支持面向对象查询。同时进行了大量的优化,才终于拥有了和SQLite一样的性能。

性能方面的担忧不用考虑了,便开始查看官方文档,Realm的官方文档也非常详细,甚至还有中文版本。

使用Realm非常简单,这里就不赘述了,有兴趣的同学可以查看官方文档。

对于移动端数据库,下面几个方面是不得不考虑的:
1)数据库版本的升级
2)是否完善的外部调试工具
3)复杂查询是否支持

Realm官方文档上面,有对这三个问题的详细解答,下面我来简单阐述一下:
第一个问题,Realm提供了Migration类实现对Realm数据库版本的升级维护。
第二个问题,Realm提供了Realm Browser的mac版本,方便在Mac机器上面进行调试。不过,遗憾的是,相关工具并没有提供Windows版本,lol 。
第三个问题,Realm有相关章节,专门介绍了多表查询方法,以及一对多,多对多数据表解决方案。

尽管如此,Realm依然有一些比较麻烦的地方,以及需要注意的地方:
1)在Activity/Fragment中使用Realm,请记得在onDestroy方法中手动关闭
2)在子线程中使用Realm,使用完请记得关闭
3)在主线程中创建的对象,子线程中不能进行操作。如果需要进行操作,请重新创建Realm对象

Realm还有一些非常优秀的地方,例如数据自动更新,自动缓存等等,这些优秀的特性也请参考官方文档。

然而,针对上面的第一个问题,对于像笔者这样比较粗心的同学,可能偶尔会忘记手动关闭Realm,从而引起内存泄露。于是,我提供了一套自己的解决方案,目前已经在Github开源,欢迎大家下载使用。

这个工程我把它取名叫 RealmHelper,RealmHelper使用Kotlin语言开发,它可以帮你自动创建Realm对象,并且在你离开Activity或者Fragment的时候手动帮你关闭Realm。来看一下它的简单用法:

class MainActivity : AppCompatActivity() {
   private var mRealmHelper: RealmHelper by Delegates.notNull()
   
   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)         
        // You must ensure activity has been created
        mRealmHelper = RealmHelper.get(this)
        // You can use all method in Realm class at here
        mRealmHelper.use { 
            executeTransaction {
            }
        }
    }
}

Kotlin语言和Java语言是互通的,你可以使用Java语言对它进行调用,也可以使用Kotlin。不过,只有在使用Kotlin语言调用的时候,你才能感受到RealmHelper的魅力。

RealmHelper会保持持续更新,不断增加新的功能,如果你在使用Realm数据库的时候发现什么比较麻烦的地方,请在文章下方的评论区域告诉我。

more:

RealmHelper工程的启发来自两个工程 Glideanko, RealmHelper的Activity/Fragment生命周期管理来自Glide的生命周期管理逻辑,RealmHelper的数据库处理逻辑来自anko的sqlite数据库处理逻辑。人类最初的知识就来源于模仿,所以也希望你能够从这个工程中有所启发,开发出自己的库。

RealmHelper : https://github.com/yuanhoujun/RealmHelper

上一篇 下一篇

猜你喜欢

热点阅读