结合RxJava更简单地使用SQLite

我经常想在项目中使用 ORM 来简化操作,但是最终都将这个念头打消。主要有以下几点原因:

  • 我的数据模型远远没有复杂到需要 ORM 帮助
  • 出于 Android 性能上的考虑,自动生成的SQL语句可能没有被优化

不过最近,我开始使用一个简单的设计模式,使用 RxJava 来提供一个简单的数据库访问管理。
我称它为 “Async Rx Read” 设计模式,很渣的一个名字哈,不过是我能想到最好的了,命名总是程序员的难题嘛。

Easy Read

Android 开发中有一条很重要的设计原则,永远不要在主线程执行 I/O 操作,显然这条规则对数据库访问也适用。
RxJava 很适用于解决这个问题。
在以前,我通常为每个 table 创建一个 Java 类,然后通过我的 SQLiteOpenHelper 来管理这些 table。
现在使用这个新的方法后,我将这个工具类扩展成所有事物读写 SQL table 的唯一入口。
我们来想个简单的例子:一个由 UserTable 类管理的 USERS 表:

// UserTable.java
List<User> getUsers(SQLiteDatabase db, String userId) {
  // select * from users where _id = {userId}
}

上面的方法有一个缺点,就是你一不小心可能会在主线程调用它,这里面是由调用者确保它是在后台线程中被执行的
(如果需要更新 UI 的话,再将结果返回给主线程)。而不是依赖一个线程池管理,或者更糟点,使用 AsyncTask 管理,
我们将使用 RxJava 替我们管理线程模型。

让我们重写这个方法返回一个 callback :

// UserTable.java
Callable<List<User>> getUsers(SQLiteDatabase db, String userId) {
  return new Callable<List<User>>() {
    @Override
    public List<User> call() {
      // select * from users where _id is userId
    }
  }
}

实际上,我们只是重构了一下这个方法返回一个 lazy result ,使得 database helper 可以将这个
result 转变成一个 Observable

// MySqliteOpenHelper.java
Observable<List<User>> getUsers(String userId) {
  return makeObservable(mUserTable.getUsers(getReadableDatabase(), userId))
    .subscribeOn(Schedulers.computation()) // note: do not use Schedulers.io()
}

注意到将 lazy result 转换成一个 Observable 时,helper 强制 subscription 运行在后台线程
(这里使用的是 computation scheduler;不要使用 Schedulers.io() 因为它是由 unbounded executor 支持的)。
这样调用者就不必担心这个方法会阻塞主线程了。

最后,makeObservable() 方法的实现很简单(而且是完全通用的):

// MySqliteOpenHelper.java
private static <T> Observable<T> makeObservable(final Callable<T> func) {
  return Observable.create(
      new Observable.OnSubscribe<T>() {
          @Override
          public void call(Subscriber<? super T> subscriber) {
            try {
              subscriber.onNext(func.call());
            } catch(Exception ex) {
              Log.e(TAG, "Error reading from the database", ex);
            }
          }
    });
}

现在,我们的 database 读取已经变成了 observables,保证查询是执行在后台线程的。
访问数据库操作也是很标准的 Rx code:

// DisplayUsersFragment.java
@Inject
MySqliteOpenHelper mDbHelper;

// ...

mDbHelper.getUsers(userId)
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Action1<List<User>>()) {
    @Override
    public void onNext(List<User> users) {
      // Update our UI with the users
    }
  }
}

如果你不需要返回结果更新你的 UI,那么你只需要 observe 一个后台线程。
然后你的 database 层会返回 observables,当获得结果时很容易将它组合变换。
例如,假定 ContactTable 是一个底层类, model (User class) 对它是不可见的,
它应该返回底层 object (可能是 Cursor 或者 ContentValues)。然后你可以用
Rx 去 map 这些底层值,把它们转换成你的 model 类,实现更加清晰的分离层。

两个提示:

  • 你的 Table 类不应该包含 public 方法:只能有 package 的 protected 方法(仅允许相同 package 内的 Helper 访问 ) 和 private 方法,
    其他类不能直接访问 Table 类。

  • 这个方法对依赖注入兼容性很好:很轻松就可以同时实现 database helper 和注入单个 Table
    (意外收获:使用 Dagger 2,你的 table 可以拥有自己的组件,因为 database helper
    是实例化它们所需的唯一参考资料 )。

这是一个很简单的设计模式,它显著提升了 RxJava 在项目中发挥的效果。
我正在扩展这层结构,让它可以为 list view adapter update 提供更灵活的通知机制
(和 SQLBrite 提供的不同),会在以后的文章中介绍。

这项工作还在进行中,欢迎反馈。


Android中调试RxJava

原文链接 : Debugging RxJava on Android 调试是查找和分析bug的过程或者预防软件的正确操作出现问题Wikipedia。 当前调试不是一件容易的事情,我们在处理Android的异步操...

发布于:1年以前  |  805次阅读  |  详细内容 »

在Android开发中使用RxJava

ReactiveX是专注于异步工作的API,它将异步事件的处理与观察者模式、迭代器模式及函数式编程相结合了起来。实时地处理返回数据是在工程中经常出现的情景,所以使用高效、可...

发布于:1年以前  |  1291次阅读  |  详细内容 »

使用RxJava缓存Rest请求

原文链接 : Subscribe It While It&#039;s Hot: Cached Rest Requests With RxJava 免责声明: 在这篇文章中,我尝试去用正确的方法来解决一个常见的问题。我仍然正在整理我脑袋...

发布于:1年以前  |  1601次阅读  |  详细内容 »

RxJava开发精要5 - Observables变换

在上一章中,我们探索了RxJava通用过滤方法。我们学习了如何使用filter()方法过滤我们不需要的值,如何使用take()得到发射元素的子集,如何使用distinct()函数来去除重复的...

发布于:1年以前  |  1027次阅读  |  详细内容 »

所属标签

最多阅读

简化Android的UI开发 7月以前  |  195112次阅读
Android设计与开发工作流 7月以前  |  2311次阅读
Google Enjarify:可代替dex2jar的dex反编译 1年以前  |  2306次阅读
Android多渠道打包工具:apptools 1年以前  |  1931次阅读
Android权限 - 第一篇 1年以前  |  1903次阅读
Google Java编程风格规范(中文版) 1年以前  |  1884次阅读
Stetho 1年以前  |  1819次阅读
Android UI基本技术点 1年以前  |  1810次阅读
30分钟搭建一个android的私有Maven仓库 1年以前  |  1775次阅读
2015 Google IO带来的新 Android 开发工具 1年以前  |  1712次阅读
你应该知道的布局和属性 1年以前  |  1662次阅读
听FackBook工程师讲*Custom ViewGroups* 1年以前  |  1657次阅读
Gradle小知识#3:任务的顺序 1年以前  |  1637次阅读
MVP在Android平台上的应用 1年以前  |  1636次阅读