ObjectBox 集成指南

7,951 阅读6分钟

ObjectBox 简介

​ 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 DaoCompat for 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 的理由。

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 的集成方式有点问题)。

ObjectBox 简单用法

​ 在 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());
    }

}

ObjectBox 的 Reactive 用法

​ 同样在 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 的方法。第一次的使用体验还是很不错的,有机会的话可以研究一下源码,探究一下高性能的原因。

欢迎关注我的公众号:cmder