ObjectBox 官网:http://objectbox.io/
以前开发项目的时候 ORM 一直用的是 GreenDao ,这次新开项目的时候访问 GreenDao 的官网的时候却发现了一行新的 Note: for new apps we recommend ObjectBox, a new object-oriented database that is much faster than SQLite and easier to use. For existing apps based on greenDAO we offer DaoCompatfor an easy switch (see also the announcement).
看来 GreenDao 这个浓眉大眼的也准备叛变革命了。。。
好久没有正儿八经地搞应用开发了,新的轮子已经出现,怎么能够停止不前,研究之,那么这个 ObjectBox 到底是何方神圣呢?
瞅一眼官网简介:ObjectBox is a super fast mobile database that persists objects. It lets you avoid many repetitive tasks and offers a simple interface to your data. 翻译一下就是:更快,更简单。翻了一下 FAQ,对比了一下 ObjectBox 和 谷歌两位亲儿子 Realm 和 Room ,如下图:
从图上可以看出除了在加载 100k 的大量数据的时候 ObjectBox 的速度慢于 Realm,在执行其他数据操作的时候 ObjectBox 的性能对其他两位都是近乎碾压式的存在。
在引入后对 apk 包的大小影响方面,ObjectBox 和 Realm 分别在 1-1.5MB 和 3-4MB ,Room 因为是对 SQLite 的封装,只有 50KB 左右。而在增加的方法数量方面,Room 的 300 个方法也远少于 Room 的 2000 个方法和 ObjectBox 的 1300 个方法。关于三者的对比,可以看这篇文章:https://notes.devlabs.bg/realm-objectbox-or-room-which-one-is-for-you-3a552234fd6e 。
如果不考虑对包的体积大小的影响,只考虑性能的话,似乎就有了选择 ObjectBox 的理由。
Note : 通常我选择 ORM 的首要条件就是支持 RxJava(是时候展示我多年RxJava脑残粉的身份了),然鹅,ObjectBox 的团队似乎对 RxJava 不太感冒,主要是介意引入RxJava 之后急剧增加的包体积和方法数,所以 ObjectBox 自己封装了一套支持 Reactive Extensions 的接口。
首先,在 Project 级别的 build.gradle 文件里添加如下脚本:
buildscript { ext.objectboxVersion = '1.5.0' repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.1.1' classpath "io.objectbox:objectbox-gradle-plugin:$objectboxVersion" } }
然后,在 Module 级别的 build.gradle 文件里添加如下脚本:
dependencies { // 这一句是添加 RxJava 扩展 compile 'io.objectbox:objectbox-rxjava:0.9.8' // 下面这两句是 ObjectBox 很骚气的一个功能——DataBrowser, 通过浏览器来调试和浏览数据库的数据 debugImplementation "io.objectbox:objectbox-android-objectbrowser:$objectboxVersion" releaseImplementation "io.objectbox:objectbox-android:$objectboxVersion" } // ObjectBox browser dependencies must be set before applying the plugin so it does not add objectbox-android // (would result in two conflicting versions, e.g. "Duplicate files copied in APK lib/armeabi-v7a/libobjectbox.so"). apply plugin: 'io.objectbox'
注意这里的 apply plugin: 'io.objectbox' 一定要添加到 dependencies 模块后面(已经踩过这个坑了,直接按照官网的 Get Started 的集成方式有点问题)。
在 Application 中初始化:
public static final String TAG = "ObjectBoxExample"; public static final boolean EXTERNAL_DIR = false; private BoxStore boxStore; @Override public void onCreate() { super.onCreate(); boxStore = MyObjectBox.builder().androidContext(App.this).build(); if (BuildConfig.DEBUG) { new AndroidObjectBrowser(boxStore).start(this); } Log.d("App", "Using ObjectBox " + BoxStore.getVersion() + " (" + BoxStore.getVersionNative() + ")"); } public BoxStore getBoxStore() { return boxStore; }
实体类格式(最简单的只要加两个注解就够了,更详细的用法可以参考官方文档):
package io.objectbox.example; import java.util.Date; import io.objectbox.annotation.Entity; import io.objectbox.annotation.Generated; import io.objectbox.annotation.Id; import io.objectbox.annotation.apihint.Internal; @Entity public class Note { // 注意这里的 @Id 注解是必须的,和 GreenDao 不同,GreenDao 可以省略,但是如果你的业务字段已经有了 一个名字为 id 的字段,可以取一个别的名字啊~ @Id long boxId; String text; String comment; Date date; public Note(long id, String text, String comment, Date date) { this.boxId = id; this.text = text; this.comment = comment; this.date = date; } public Note() { } public long getId() { return this.boxId; } public void setId(long id) { this.boxId = id; } public String getText() { return this.text; } public void setText(String text) { this.text = text; } public String getComment() { return this.comment; } public void setComment(String comment) { this.comment = comment; } public Date getDate() { return this.date; } public void setDate(Date date) { this.date = date; } }
在 Activity 执行查询(多余的业务代码已经被我省略):
public class NoteActivity extends Activity { private Box<Note> notesBox; private Query<Note> notesQuery; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); BoxStore boxStore = ((App) getApplication()).getBoxStore(); notesBox = boxStore.boxFor(Note.class); // query all notes, sorted a-z by their text (http://greenrobot.org/objectbox/documentation/queries/) notesQuery = notesBox.query().order(Note_.text).build(); updateNotes(); } /** Manual trigger to re-query and update the UI. For a reactive alternative check {@link ReactiveNoteActivity}. */ private void updateNotes() { List<Note> notes = notesQuery.find(); } private void addNote() { Note note = new Note(); note.setText(noteText); note.setComment(comment); note.setDate(new Date()); notesBox.put(note); Log.d(App.TAG, "Inserted new note, ID: " + note.getId()); } }
同样在 Activity 中执行查询:
/** An alternative to {@link NoteActivity} using a reactive query (without RxJava, just plain ObjectBox API). */ public class ReactiveNoteActivity extends Activity { private Box<Note> notesBox; private Query<Note> notesQuery; private DataSubscriptionList subscriptions = new DataSubscriptionList(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); notesBox = ((App) getApplication()).getBoxStore().boxFor(Note.class); // query all notes, sorted a-z by their text // (http://greenrobot.org/objectbox/documentation/queries/) notesQuery = notesBox.query().order(Note_.text).build(); // Reactive query (http://greenrobot.org/objectbox/documentation/data-observers- reactive-extensions/) notesQuery.subscribe() .onError(new ErrorObserver() { @Override public void onError(Throwable th) { } }) // 官方推荐的做法是对 data observers 持有弱引用,防止忘记 cancel subscriptions, // 但是最好还是记得及时 cancel subscriptions(例如在 onPause、onStop 或者 // onDestroy 方法中) .weak() .on(AndroidScheduler.mainThread()) .observer(new DataObserver<List<Note>>() { @Override public void onData(List<Note> notes) { // 只要数据库里的数据发生了变化,这里的方法就会被回调执行,相当智能。。。 // 业务代码 } }); } @Override protected void onDestroy() { subscriptions.cancel(); super.onDestroy(); } private void addNote() { Note note = new Note(); note.setText(noteText); note.setComment(comment); note.setDate(new Date()); notesBox.put(note); Log.d(App.TAG, "Inserted new note, ID: " + note.getId()); } }
上面的用法看上去就像傻瓜版的 RxJava,上手容易,概念理解也简单,但是并没有 RxJava那么强大的功能,所以如果在应对更复杂的业务逻辑的时候,还是需要引入 RxJava ,示例如下:
Query query = box.query().build(); RxQuery.observable(query).subscribe(this);
RxQuery 可以使用 Flowable、Observable、Single 来订阅查询结果,目前 ObjectBox 只支持 RxJava 2 。
添加权限
<uses-permission android:name="android.permission.INTERNET" />
在 Application 开启调试
boxStore = MyObjectBox.builder().androidContext(App.this).build(); if (BuildConfig.DEBUG) { boolean started = new AndroidObjectBrowser(boxStore).start(this); Log.i("ObjectBrowser", "Started: " + started); }
执行命令
adb forward tcp:8090 tcp:8090
在电脑浏览器中访问
http://localhost:8090/index.html
效果贼6:
最关键的问题是,如果 put、find 这些方法全是同步的,对于大量数据的存和查都是耗时操作,如果直接写在主线程会阻塞主线程,尤其是 find 方法,而 ObjectBox 的 Reactive 封装显然没有 RxJava 那么强大,GreenDao对RxJava的支持非常好,如果要封装数据库框架的话进行线程切换非常方便,但是我在ObjectBox的官方文档里暂时还没发现对各个方法的执行线程的明确说明。我已经在 Github 提了 issue ,不过还没人回我,有待继续研究。
集成方便简单,调试效果拔群,终于不用再用 DDMS + SQLite Export 调试了(每次手动导数据那叫一个痛苦,后来听说加了一个自动同步的功能,没细研究,不过跟 ObjectBox 的 DataBroswer 是没法比),调试效果很棒,不用写 SQL 。但是感觉仍有继续改进的空间,简单化的同时也牺牲了一部分功能的强大和灵活性,相比较而言可能 GreenDao 会更成熟一点,当然官网也提供了从 GreenDao 转到 ObjectBox 的方法。第一次的使用体验还是很不错的,有机会的话可以研究一下源码,探究一下高性能的原因。