ObjectBox 使用指南

转自: https://juejin.cn/post/6844903618676523022

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 ,如下图:

image.png

从图上可以看出除了在加载 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:

image.png

问题

最关键的问题是,如果 put、find 这些方法全是同步的,对于大量数据的存和查都是耗时操作,如果直接写在主线程会阻塞主线程,尤其是 find 方法,而 ObjectBox 的 Reactive 封装显然没有 RxJava 那么强大,GreenDao对RxJava的支持非常好,如果要封装数据库框架的话进行线程切换非常方便,但是我在ObjectBox的官方文档里暂时还没发现对各个方法的执行线程的明确说明。我已经在 Github 提了 issue ,不过还没人回我,有待继续研究。

体会

集成方便简单,调试效果拔群,终于不用再用 DDMS + SQLite Export 调试了(每次手动导数据那叫一个痛苦,后来听说加了一个自动同步的功能,没细研究,不过跟 ObjectBox 的 DataBroswer 是没法比),调试效果很棒,不用写 SQL 。但是感觉仍有继续改进的空间,简单化的同时也牺牲了一部分功能的强大和灵活性,相比较而言可能 GreenDao 会更成熟一点,当然官网也提供了从 GreenDao 转到 ObjectBox 的方法。第一次的使用体验还是很不错的,有机会的话可以研究一下源码,探究一下高性能的原因。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容