ObjectBox 官網:http://objectbox.io/html
之前開發項目的時候 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 DaoCompat for an easy switch (see also the announcement).java
看來 GreenDao 這個濃眉大眼的也準備叛變革命了。。。react
很久沒有正兒八經地搞應用開發了,新的輪子已經出現,怎麼可以中止不前,研究之,那麼這個 ObjectBox 究竟是何方神聖呢?android
瞅一眼官網簡介: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 ,以下圖:git
從圖上能夠看出除了在加載 100k 的大量數據的時候 ObjectBox 的速度慢於 Realm,在執行其餘數據操做的時候 ObjectBox 的性能對其餘兩位都是近乎碾壓式的存在。github
在引入後對 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 的理由。api
Note : 一般我選擇 ORM 的首要條件就是支持 RxJava(是時候展現我多年RxJava腦殘粉的身份了),然鵝,ObjectBox 的團隊彷佛對 RxJava 不太感冒,主要是介意引入RxJava 以後急劇增長的包體積和方法數,因此 ObjectBox 本身封裝了一套支持 Reactive Extensions 的接口。瀏覽器
首先,在 Project 級別的 build.gradle 文件裏添加以下腳本:bash
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 的方法。第一次的使用體驗仍是很不錯的,有機會的話能夠研究一下源碼,探究一下高性能的緣由。