Android MVVM——Room实现数据存储底层

存储模式

在开发移动应用程序的许多情况下,我们还需要提供对数据的脱机访问。想象一下,我们正在开发一个新闻阅读类APP,并且您还希望您的用户可以在他们乘坐飞机时或者他们在没有互联网访问的任何区域访问数据。在某些情况下,您希望显示存储的数据,同时从API加载新数据。如果从应用程序开始就没有在体系结构中实现或考虑这一点,这可能会成为一项复杂的任务,并且可能需要进行大量更改,这也会影响UI层,为新的潜在错误留出空间在你的代码。存储模式正好解决了这个问题,我们从应用程序的开头实现了它。

为什么使用数据存储模式?

  • 将应用程序与数据源分离
  • 提供来自多个源(DB,API)的数据
  • 隔离数据层
  • 集中,一致的数据访问方式
  • 通过单元测试可测试业务逻辑
  • 很容易增加新数据源

如何使用数据库

在Android中,我们有很多操作数据库的库:

  • 原生SQLite(太多的样板代码)
  • Realm(太复杂了,我们不需要它的大部分功能)
  • GreenDao(非常好用的ORM)
  • Room(Google官方支持的新的ORM框架)

这个例子里面,我准备使用Room来演示。

Room 的一些特点

  1. 编译时 sql 语句检查。相信大家都有过 app 跑起来,执行到 db 语句的时候 crash,检查之后发现原来是 sql 语句少了一个 ) 或者其它符号之类的经历。Room 会在编译阶段检查你的 DAO 中的 sql 语句,如果写错了(包括 sql 语法错误跟表名、字段名等等错误),会直接编译失败并提醒你哪里不对。
  2. sql 查询直接关联到 Java 对象。这个应该不用详细解释了,虽然很多第三方 db 库早已经实现。
  3. 耗时操作主动要求异步处理。这一点还是挺值得注意的,Room 会在执行 db 操作时判断是不是在 UI 线程,比如当你需要插入一条记录到数据库时,Room 会让你放到异步线程去做,否则会直接 crash 掉 app 来告诉你不这样做容易阻塞 UI 线程。虽说死相难看了点(个人觉得打个警告不就完了么?),但对于开发者开发出高质量的应用还是有帮助的。
  4. 基于注解编译时自动生成代码。这个应该算是 Room 工作原理的核心所在了,你要写的代码之所以这么少,说白了还不是因为 Google 给你写好了很多?希望以后有时间能写一篇源码分析出来,那个时候再讲吧。
  5. API 设计符合 Sql 标准。方便扩展进行各种 db 操作。

Room的使用

首先创建User的数据类,使用@Entity注解建表。

@Entity(tableName = "users")
data class User(
    @PrimaryKey
    @ColumnInfo(name = "email")
    val email: String,
    
    @ColumnInfo(name = "name")
    val name: String
)

现在我们要声明数据库和UserDao。

@DataBase(entities = [User::class], version = 1)
abstract class AppDataBase: RoomDataBase(){
    abstract fun userDao():UserDao
}

@Dao
interface UserDao{
    @Query("SELCET * FROM users")
    fun getUsers(): Single<List<User>>
    
    @Insert(onConfilct = OnConflictStrategy.REPLACE)
    fun insert(user: User)
    
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertAll(users: List<User>)
}

接下来,我们使用Room来创建数据库(通常在项目中会使用Dagger依赖注入的方式)。

val appDataBase = Room.databaseBuilder(applicationContext,
                                      AppDataBase::class.java,"mvvm-database").builde()

val userDao = appDatabase.userDao()

可以看得出来,使用Room相比其它的数据库框架还是非常非常简单的。现在我们可以对我们的UserRepository进行改造了,加入DataBase数据源。

class UserRepository(val userApi: UserApi, val userDao: UserDao){
    fun getUsers(): Observable<List<User>>{
        return Observable.concatArray(
            getUsersFromDb(),
            getUsersFromApi()
        )
    }
    
    fun getUsersFromDb(): Observabel<List<User>>{
        return userDao.getUsers().filter { it.isNotEmpty() }
            .toObservable()
            .doOnNext{
                //获取数据成功
            }
    }
    
    fun getUserFromApi(): Observable<List<User>>{
        return userApi.getUsers()
            .doOnNext{
                ...
                storeUsersInDb(it)
            }
    }
    
    fun storeUserInDb(users: List<User>){
        Observable.fromCallable{ userDao.insertAll(users) }
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .subscribe {
                //保存成功
            }
    }
}

我们使用Observable.concatArray()并传递我们的2个源,即DB和API Observables。这将首先向用户提供来自DB的数据,其次是来自API的数据。当从API接收数据时,我们还在doOnNext()内部触发异步操作以将数据存储在我们的DB中。

可以看到,我们非常简单的实现了一系列的数据存储操作,并且完全与View层脱离了关系。

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

推荐阅读更多精彩内容