从ActiveAndroid到Realm的爬坑之路(一)
来源:互联网 发布:淘宝店铺认证可以改吗 编辑:程序博客网 时间:2024/06/05 13:04
公司的项目一直都是使用的ActiveAndroid,这是一个很传统的ORM框架,不过无论是与Gson的配合使用,还是连接查询或者分页查询,传统数据库的理念它都能支持的很好。无奈的是这个项目在GitHub上的最近更新时间还是在两年前,它对AndroidStudio的InstantRun特性支持的不是很好(编译时间就是程序员的生命啊,感谢Google推出的这一神器 )。强行使用的话可能会报如下错误:
第二点是它的版本升级策略做的不好,不能做到跨版本升级,目前的做法只能是升级APP的后如果检测到先前的版本就卸载然后重装,原先的数据只能丢失了。这样简单粗暴的做法自然不是长久之计。
第三点是不能查询个别列,将所有字段都查出来既浪费时间,又浪费空间。
ActiveAndroid的罪过数落完了,自然要把更换数据库的Task提上日程。leader给新的数据库提了五点要求:
1. 效率高,支持事务。
2. 支持个别列查询。
3. 升级方便,能跨版本升级。
4. 支持联表操作。
5. 逐步替换,替换过程中能随时切回原来的ORM。
最难的一点是更换只能在Service层进行(项目中所有的数据库操作都是通过一个一个Service完成的),不能对项目进行大量修改这一限制,注定了使用Realm会遇到一个又一个的坑。
Realm确实是一个非常前沿的数据库框架,跟其它ORM的千遍一律不同,它提供了强大的功能和可靠的速度,它的优点可以作如下总结:
- 效率高,因为是自己实现的C++数据库,与SQLite底层使用java不同,它天生就比其高效快速。维护Realm数据库还能带来平台无关性,iOS、Android等平台都可以共享数据库。
- Auto-Refresh,Realm为每个实体创建了代理类,并且实现了其Getter/Setter,因此,每次得到或者修改实体的值,都是直接对底层数据库进行的操作,官方将其称为”懒加载”。但是很好奇它是怎么做到在下个Looper事件中自动更新的,因此进入源码查看了一番:
要想自动更新,那肯定需要在线程中创建Handler,首先找到Realm.getInstance(),是不是在Realm初始化的时候绑定Handler到当前线程:
public static Realm getDefaultInstance() { if (defaultConfiguration == null) { throw new NullPointerException("No default RealmConfiguration was found. Call setDefaultConfiguration() first"); } return RealmCache.createRealmOrGetFromCache(defaultConfiguration, Realm.class); }
这里可以看到,每个线程只能拥有一个Realm实例,再点进createRealmOrGetFromCache():
static synchronized <E extends BaseRealm> E createRealmOrGetFromCache(RealmConfiguration configuration,Class<E> realmClass) { ······ //这个类保存了Realm 和当前线程使用getInstance()的计数器,以及全局计数器。 RefAndCount refAndCount = cache.refAndCountMap.get(RealmCacheType.valueOf(realmClass)); if (refAndCount.localRealm.get() == null) { // Create a new local Realm instance BaseRealm realm; if (realmClass == Realm.class) { // RealmMigrationNeededException might be thrown here. //在这里创建了新对象 realm = Realm.createInstance(configuration, cache.typedColumnIndices); } else if (realmClass == DynamicRealm.class) { realm = DynamicRealm.createInstance(configuration); } else { throw new IllegalArgumentException(WRONG_REALM_CLASS_MESSAGE); } // The cache is not in the map yet. Add it to the map after the Realm instance created successfully. if (!isCacheInMap) { cachesMap.put(configuration.getPath(), cache); } //创建一个Realm实例后,保存并将计数器置零。 refAndCount.localRealm.set(realm); refAndCount.localCount.set(0); } Integer refCount = refAndCount.localCount.get(); if (refCount == 0) { ... //全局计数器加1 refAndCount.globalCount++; } //线程计数器加1 refAndCount.localCount.set(refCount + 1); @SuppressWarnings("unchecked") E realm = (E) refAndCount.localRealm.get(); return realm;
所以在一个线程中可以多次使用getInstance(),并不会影响性能,但是从close() 方法的源码来看,调用一次close(),也只是将计数器减一,只有计数器归零,才会执行回收工作。也就是说,同一线程,用了几次getInstance 就要调几次 close ,否则就会内存泄漏,而且泄漏的是c++底层的那部分,这设计的就比较尴尬了。
接着走,刚才看到,对于第一次调用getInstance,会调用createInstance, 那跳进这个方法看看:
static Realm createInstance(RealmConfiguration configuration, ColumnIndices columnIndices) { try { return createAndValidate(configuration, columnIndices); } catch (RealmMigrationNeededException e) { if (configuration.shouldDeleteRealmIfMigrationNeeded()) { deleteRealm(configuration); } else { try { migrateRealm(configuration); } catch (FileNotFoundException fileNotFoundException) { // Should never happen throw new RealmIOException(fileNotFoundException); } } return createAndValidate(configuration, columnIndices); } }
并没有什么有用的信息,只是抛出版本不匹配异常时,会根据flag删除或更新以前的Realm,关键还是createAndValidate() :
static Realm createAndValidate(RealmConfiguration configuration, ColumnIndices columnIndices) { Realm realm = new Realm(configuration); ... return realm; }
只需要关注这两句,调用了Realm的构造函数,而它的构造函数什么都没做,只是调用了它的父类BaseRealm的构造:
protected BaseRealm(RealmConfiguration configuration) { //终于发现了线程相关的东西和AutoRefresh this.threadId = Thread.currentThread().getId(); ... //handlerController 就是Handler.Callback this.handlerController = new HandlerController(this); if (handlerController.isAutoRefreshAvailable()) { setAutoRefresh(true); } } public void setAutoRefresh(boolean autoRefresh) { checkIfValid(); handlerController.checkCanBeAutoRefreshed(); if (autoRefresh && !handlerController.isAutoRefreshEnabled()) { // Switch it on //这里创建了Handler,与当前线程绑定到了一起。而AutoRefresh就是在handlerController中实现的。 handler = new Handler(handlerController); handlers.put(handler, configuration.getPath()); } else if (!autoRefresh && handlerController.isAutoRefreshEnabled() && handler != null) { // Switch it off removeHandler(); } handlerController.setAutoRefresh(autoRefresh); }
至此,源码的探究应该告一段落了,接着列举一些Realm的特点:
3. 文档完善算是最吸引我的一点了,终于不用使用Google翻译然后边猜边读了,英语差的孩子伤不起。
4. JSON支持, 能直接读取json并且写入数据库,怎么说呢,ActiveAndroid配合Gson也能实现这一点,但程序员的事,不优雅点怎么行? 一句createObjectFromJson 就搞定一切的感觉还是很美好的。
5. 其余的还有加密,多线程支持,以及官方说的多进程支持特性,虽然听着很带感,不过暂时还没试过。
总之,一个项目若是处于设计初期,我觉得使用Realm还是很方便的,毕竟,老技术虽然稳定,但总会有各种无法解决的缺陷,而新技术,正是为了解决这些缺陷而来的。
- 从ActiveAndroid到Realm的爬坑之路(一)
- 从ActiveAndroid到Realm的爬坑之路(一)
- 从ActiveAndroid到Realm的爬坑之路(二)
- 从2003到2005的之路(一)
- react-native之Realm数据库的使用(一)
- Android ReactNative使用realm走过的坑(一)-Missing Realm constructor
- ActiveAndroid (一):下载、配置与创建
- Realm初探(一)
- Android之数据库框架ActiveAndroid的使用
- 我的技术转型之路——从C++到Android (一)
- Realm数据库 从入门到“放弃”
- android之RxJava的学习,从浅到深,从入门到别放弃(一)
- 【iOS】基于Realm数据库的记账软件--Realm数据库(一)
- 从校园到工作的路(一)
- Realm android 使用(一)
- 第六章 Realm及相关对象(一) Realm
- 从抽象到模式——面向对象之旅(一)、抽象的魅力
- [从菜鸟到高手演练]之Linux下shell脚本的使用(一)
- HDU2577[How to Type] 动态规划
- 为什么使用 Redis及其产品定位
- matlab2c使用c++实现matlab函数系列教程-normrnd函数
- IDEA运行Maven项目部署tomcat上
- java反射之获取class对象的三种方式
- 从ActiveAndroid到Realm的爬坑之路(一)
- Aggressive cows
- Python学习记录-----批量发送post请求
- thinkphp3.2.3 ueditor 后台配置项返回格式出错,上传功能将不能正常使用!
- 素数算法总结
- 获取JAVA(WEB)项目路径的方法
- -Java基础-Java介绍
- 关于推送的一些问题
- 二叉树前中后递归算法