当前位置: 首页 > news >正文

Android开发的这一年里,Jetpack的Room源码是怎么狠狠奖励我的?

简述

Android Jetpack的出现统一了Android开发生态,各种三方库逐渐被官方组件所取代。Room也同样如此,逐渐取代竞品成为最主流的数据库ORM框架。这当然不仅仅因为其官方身份,更是因为其良好的开发体验,大大降低了SQLite的使用门槛。

Room是Google官方在SQLite基础上封装的一款数据持久库,是Jetpack全家桶的一员,和Jetpack其他库有着可以高度搭配协调的天然优势。Room使用APT技术,大大简化了使用SQLite的代码量,只需使用注解配合少量代码即可实现高效的数据库操作。

Room基本介绍

框架特点

相对于SQLiteOpenHelper等传统方法,使用Room操作SQLite有以下优势:

  • 编译期的SQL语法检查
  • 开发高效,避免大量模板代码
  • API设计友好,容易理解
  • 可以与RxJava、 LiveData 、 Kotlin Coroutines等进行桥接

添加依赖

dependencies {implementation "androidx.room:room-runtime:2.2.5"kapt "androidx.room:room-compiler:2.2.5"
}

基本组件

Room的使用,主要涉及以下3个组件

  • Database: 访问底层数据库的入口
  • Entity: 代表数据库中的表(table),一般用注解
  • Data Access Object (DAO): 数据库访问者

这三个组件的概念也出现在其他ORM框架中,有过使用经验的同学理解起来并不困难: 通过Database获取DAO,然后通过DAO查询并获取entities,最终通过entities对数据库table中数据进行读写

实例实战

​
insert:使用注解 @Insert,Room 会自动将所有参数在单个事物中插入数据库
@Insert
public fun inertUser (user: User)  // 单个参数可以返回 long
​
@Insert
public fun insertUserList (array: Array<User>)  // 参数为集合可以返回 long []
数据库添加 User
val user = User()
user.name = "赵云 编号 = $number"
val address = Address()
address.street = "成都接头"
address.state = "蜀汉"
address.city = "常山"
address.postCode = 10010
user.address = address
userDao.inertUser (user)  // 添加 User
添加数据结果:
​
​
upadte:使用 @Update 注解
@Update
public fun update (user: User)   // 可以让此方法返回一个 int 值,表示数据库中更新的行数  val user = User()
user.id = 1
user.name = "张翼德"
address.city = "涿郡"
.....
userDao.update(user)
点击 Update 后再查询结果:此时的赵云已经改为张翼徳了
​
​
delete:使用 @Delete 注解
@Delete 
public fun delete (user: User)   // 可以返回一个 int 值,表示从数据库中删除的行数val user = User()
user.id = 1    // 要删除的主键 id
userDao.delete(user)
点击 delete 后再次查询数据:编号为 1 的数据已被删除
​
​
查询信息 :@Query 注解对数据库执行读 / 写操作
@Query("SELECT * FROM user")
public fun selectAll (): Array<User>   // 查询所有数据@Query("SELECT * FROM user WHERE name = :name")
public fun selectUser (name:String): Array<User>   // 条件查询
返回列的子集:创建子类在每个属性中使用 @ColumnInfo (name = "name") 标记对应数据库中的列名
public class UserTuple {                  // 1、根据要查询的字段创建 POJO 对象      @ColumnInfo(name = "name")public var name: String? = null@ColumnInfo(name = "city")public var city: String? = null
}@Query ("SELECT name ,city FROM user")  // 2、查询的结果会映射到创建的对象中public List<UserTuple> loadFullName();val userList = userDao.loadFullName()
for (userTuple in userList) {stringBuilder.append(userTuple.name).append("  ").append(userTuple.city).append("\n")
}
输出的结果:只有 name 和 city 两列
​
​
范围条件查询 :查询城市中所有用户
@Query("SELECT name ,street FROM user WHERE city IN (:cityArray)")
fun loadUserInCity(cityArray: Array<String>): List<UserTuple>val userList = userDao.loadUserInCity (arrayOf ("常山"))  // 查询常山,只会出现赵云不会出现张翼德
​
​
Observable 查询:使用 LiveData 作为查询方法的返回值,注册观察者后,数据表更改时自动更新 UI
@Query("SELECT name ,street FROM user WHERE city IN (:cityArray"))
fun loadUserInCityLive(cityArray: Array<String>): LiveData<List<UserTuple>>
​
​
​
private lateinit var liveData: LiveData<Array<UserTuple>>  // 定义一个 LiveData
get() {
return userDao.loadUserInCityLive (arrayOf ("常山"))
}
​
val observer = Observer<Array<UserTuple>> {    // 定义一个观察者val stringBuilder = StringBuilder()for (index in it!!.indices) {val userTuple = it[index]stringBuilder.append(userTuple.name).append("  ").append(userTuple.name).append("  \n")}tv_main_show.text = stringBuilder.toString()
}
liveData.observe (this, observer)   // 注册观察者
运行结果:此时当添加数据时,UI 会自动更新;
​
RxJava 查询 :返回 Observable 实例可以使用 RxJava 订阅观察者
@Query("SELECT * FROM user WHERE id = :id LIMIT 1")
fun loadUserRxJava(id:Int) : Flowable<User>
​
​
userDao.loadUserRxJava(4).subscribe(Consumer {val stringBuilder = StringBuilder()stringBuilder.append(it.id).append("  ").append(it.name).append("  \n")tv_main_show.text = stringBuilder.toString()})
​
​Cursor 查询:返回 Cursor 对象
fun loadUserCursor(id:Int) : Cursor
多表查询:根据表的外键多表查询
@Query("SELECT user.name AS userName, pet.name AS petName "+ "FROM user, pet "+ "WHERE user.id = pet.user_id")

源码分析

从上面的Demo代码可以看出,Room有很多的注解,实际上Room正是通过APT注解处理器,自动生成了许多代码,避免使用者在为了使用数据库编写重复的模板代码。

1.Room.databaseBuilder.build()

数据库使用的入口,也就是构造RoomDatabase的实例,它里面会根据buider中做的配置,进行一系列赋值操作,生成一个 DatabaseConfiguration对象,然后传入的 class 对象,调用Class.newInstance()方法,获取到RoomDatabase的实例。 接着就是调用RoomDatabase.init进行初始化操作。

public T build() {//...省略DatabaseConfiguration configuration =new DatabaseConfiguration(mContext,mName,factory,mMigrationContainer,mCallbacks,mAllowMainThreadQueries,mJournalMode.resolve(mContext),mQueryExecutor,mTransactionExecutor,mMultiInstanceInvalidation,mRequireMigration,mAllowDestructiveMigrationOnDowngrade,mMigrationsNotRequiredFrom,mCopyFromAssetPath,mCopyFromFile,mCopyFromInputStream,mPrepackagedDatabaseCallback,mTypeConverters);T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);db.init(configuration);return db;
}

2.RoomDatabase.init()

调用了createOpenHelper方法,createOpenHelper方法实现在AppDatabase_Impl中,创建了RoomOpenHelper,RoomOpenHelper继承SupportSQLiteOpenHelper.Callback。 引申:注意这里的 AutoCloser对象,这里没有仔细研究它的代码,但是它应该是代理持有了一个 SupportSQLiteOpenHelper对象,可以实现在提交数据库事务之后,自动的判断并close数据库。 从这里就可以看出,Room实际上是对 SQLite的再次封装,但是通过 APT 以及其他辅助类,使得Room的比直接用SQLite要简便很多。

@Overrideprotected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration,new RoomOpenHelper.Delegate(2) {//...省略}
}
​
@CallSuper
public void init(@NonNull DatabaseConfiguration configuration) {mOpenHelper = createOpenHelper(configuration);
​// Configure SqliteCopyOpenHelper if it is available:SQLiteCopyOpenHelper copyOpenHelper = unwrapOpenHelper(SQLiteCopyOpenHelper.class,mOpenHelper);if (copyOpenHelper != null) {copyOpenHelper.setDatabaseConfiguration(configuration);}
​AutoClosingRoomOpenHelper autoClosingRoomOpenHelper =unwrapOpenHelper(AutoClosingRoomOpenHelper.class, mOpenHelper);
​if (autoClosingRoomOpenHelper != null) {mAutoCloser = autoClosingRoomOpenHelper.getAutoCloser();mInvalidationTracker.setAutoCloser(mAutoCloser);}  // ... 省略
}            

3.AppDatabase.getUserDao()

AppDatabase是我们继承自RoomDatabase的抽象类,我们在里面添加了获取UserDao的方法,Room 会通过APT技术,在编译之后自动帮我们添加对应的实现。

@Overridepublic UserDao getUerDao() {if (_userDao != null) {return _userDao;} else {synchronized(this) {if(_userDao == null) {_userDao = new UserDao_Impl(this);}return _userDao;}}}

4.UserDao_Impl

可以看出来,UserDao_Impl根据注解,具体的实现了我们在 UserDao接口中添加的方法,其中的关键点就在于,根据我们添加在注解中的 sql 语句,生成了对数据库的访问代码。

@Override
public void insertUserRecord(final User user) {__db.assertNotSuspendingTransaction();__db.beginTransaction();try {__insertionAdapterOfUser.insert(user);__db.setTransactionSuccessful();} finally {__db.endTransaction();}
}
​
@Override
public User geUerById(final String id) {final String _sql = "select *from User where id = (?)";final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);int _argIndex = 1;if (id == null) {_statement.bindNull(_argIndex);} else {_statement.bindString(_argIndex, id);}__db.assertNotSuspendingTransaction();final Cursor _cursor = DBUtil.query(__db, _statement, false, null);try {final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");final User _result;if(_cursor.moveToFirst()) {final String _tmpId;if (_cursor.isNull(_cursorIndexOfId)) {_tmpId = null;} else {_tmpId = _cursor.getString(_cursorIndexOfId);}final String _tmpName;if (_cursor.isNull(_cursorIndexOfName)) {_tmpName = null;} else {_tmpName = _cursor.getString(_cursorIndexOfName);}_result = new User(_tmpId,_tmpName);} else {_result = null;}return _result;} finally {_cursor.close();_statement.release();}
}

总结

本文只是对Room的一个简单分析,正如在一开始的概述里面说的那样:Room是对SQLite数据库的抽象,它提供了很多便利的API和注解等,简化了使用者使用数据库的方式。本文没有分析 Room和 LiveData结合使用的情况,因为笔者公司的项目还没能引入 LiveData。

抛开这一点不谈,个人认为它有两个比较显著的优点: 1、当然就是通过APT技术,TypeConverter 等,简化了使用,减少了大量的访问数据库的模板代码。 2、是文中也有提到的,它对数据库升级的优化,除了可以避免两个分支同时升级数据库但是合并不冲突导致的错误之外,它还提供了对数据库升级做单元测试的工具类,安全性提升很多。

相关文章:

Android开发的这一年里,Jetpack的Room源码是怎么狠狠奖励我的?

简述 Android Jetpack的出现统一了Android开发生态&#xff0c;各种三方库逐渐被官方组件所取代。Room也同样如此&#xff0c;逐渐取代竞品成为最主流的数据库ORM框架。这当然不仅仅因为其官方身份&#xff0c;更是因为其良好的开发体验&#xff0c;大大降低了SQLite的使用门槛…...

推荐一款卸载软件的小工具-《UninstallToo》

目录 UninstallToo介绍 UninstallToo下载 UninstallToo使用 总结 UninstallToo介绍 Uninstall Tool 是一款可以用来替代“添加/删除程序”的工具。它允许您显示隐藏的安装程序&#xff0c;按名称过滤已安装程序的列表&#xff0c;强行写在程序&#xff0c;浏览注册表项目&a…...

线程池——JUC随记8

线程池使用方式 1、一池N线程&#xff08;Executors.newFixedThreadPool(n)&#xff09; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class ExecutorDemo {public static void main(String[] args) {ExecutorService execu…...

SpringCloudAlibaba微服务调用组件-Feign

SpringCloudAlibaba微服务调用组件-Feign 本项目代码与笔记已存放在Gitee仓库 地址&#xff1a; 代码&#xff0c;笔记 文章目录SpringCloudAlibaba微服务调用组件-Feign1. 什么是Feign1.1 优势2. Spring Cloud Alibaba快速整合OpenFeign1&#xff09;引入依赖2&#xff09;编写…...

高效学习方法论

2023.03.17 《程序员的三门课&#xff1a;技术精进、架构修炼、管理探秘 / 于君泽等著》学习笔记 学会学习一、高效学习的方法1、管理好自己的目标1&#xff09;评估能力2&#xff09;制定目标3&#xff09;评估目标2、利用好碎片时间3、在同一时间只做一件事二、高效学习的途径…...

C语言结构体(一篇学会)

C语言结构体 在C语言中&#xff0c;结构体是一种自定义的数据类型&#xff0c;它允许用户将不同类型的数据组合在一起。结构体由多个变量组成&#xff0c;这些变量称为结构体的成员。结构体成员可以是不同的数据类型&#xff0c;如整数、浮点数、字符或其他结构体等。 结构体…...

嵌入式软件开发之Linux下C编程

目录 前沿 Hello World&#xff01; 编写代码 编译代码 GCC编译器 gcc 命令 编译错误警告 编译流程 Makefile 基础 何为 Makefile Makefile 的引入 前沿 在 Windows 下我们可以使用各种各样的 IDE 进行编程&#xff0c;比如强大的 Visual Studio。但是在Ubuntu 下如何进…...

普通Java工程师 VS 优秀架构师

1 核心能力 1.1 要成为一名优秀的Java架构师 只懂技术还远远不够&#xff0c;懂技术/懂业务/懂管理的综合型人才&#xff0c;才是技术团队中的绝对核心。 不仅仅是架构师&#xff0c;所有的技术高端岗位&#xff0c;对人才的综合能力都有较高的标准。 架构路线的总设计师 规…...

Java:SpringBoot实现ApplicationEvent事件的监听和发布

通过发布订阅模式实现数据的异步处理&#xff0c;比如异步处理邮件发送 新建SpringBoot项目 项目结构 . ├── pom.xml └── src└── main├── java│ └── com│ └── example│ └── demo│ ├── Application.java│ …...

星戈瑞-Sulfo-Cyanine3 azide?磺酸基-Cy3-N3叠氮基水溶性染料

Sulfo-Cyanine3 azide&#xff1f; 品牌&#xff1a;星戈瑞 CAS号&#xff1a;2055138-89-9 外观&#xff1a; 暗红色晶体 分子量&#xff1a;720.83 分子式&#xff1a;C34H45N6NaO8S2 纯度&#xff1a;95% 储藏条件&#xff1a;-20C 下避光保存 Sulfo-Cyanine3 azide 是一种…...

十大经典排序算法(下)

&#x1f353;个人主页&#xff1a;bit.. &#x1f352;系列专栏&#xff1a;Linux(Ubuntu)入门必看 C语言刷题 数据结构与算法 HTML和CSS3 目录 1.6 快速排序 1. 算法步骤 2. 动图演示 3.代码实现 1.7 堆排序 1. 算法步骤 2. 动图演示 3. 代码实现 1.8 计数排…...

网络协议分析期末复习(四)

目录 0.前言 1.IP层对改善TCP性能支持的机制 2.TCP防止半开放连接的机制 3.TCP协议中强推位&#xff08;P&#xff09;和紧急位&#xff08;U&#xff09;的用法 4.TCP的流量控制和拥塞控制的异同点 异&#xff1a; &#xff08;1&#xff09;两者的特点不同&#xff1a;…...

Matlab对图像和视频的简单处理(图像视频文件读取和输出,转灰度图,取指定帧的图像)

文章目录1.图像文件的读取2.图像效果展示3.将彩色图转换为灰度图4.视频文件的读取5.读取视频中指定帧的图像6.图片文件的报错1.图像文件的读取 语法介绍&#xff1a; A imread(filename) A imread(filename, fmt)参数介绍&#xff1a; filename&#xff1a;要读取的图像文…...

ArrayList源码分析

ArrayList源码分析目标:一、 ArrayList的简介二、ArrayList原理分析2.1 ArrayList的数据结构源码分析2.2 ArrayList默认容量&最大容量2.3 为什么ArrayList查询快&#xff0c;增删慢&#xff1f;2.4 ArrayList初始化容量1、创建ArrayList对象分析&#xff1a;无参数2、创建A…...

SpringBoot IOC、DI、@Autowired、@Resource、作用域

一、初识Spring1.1 Spring是什么Spring是一个轻量级Java开发框架&#xff0c;目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的开源框架&#xff0c;为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构&#xff0c;Java开发者可以专…...

链表相关oj题

1.Leetcode203 移除链表元素 解题思路&#xff1a;从头节点开始进行元素删除&#xff0c;每删除一个元素&#xff0c;需要重新链接节点 struct ListNode* removeElements(struct ListNode* head, int val){struct ListNode*dummyheadmalloc(sizeof(struct ListNode));dummyhea…...

【Linux】操作系统(Operator System)

操作系统&#xff08;Operator System &#xff09;一、操作系统的概念二、操作系统的作用三、系统调用和库函数一、操作系统的概念 操作系统是一组控制和管理计算机软硬件资源&#xff0c;为用户提供便捷使用的计算机程序的集合&#xff0c;是配置在计算机硬件系统上的第一层…...

机器学习自学笔记——感知机

感知机预备知识 神经元 ​ 感知机算法最初是由科学家从脑细胞的神经凸起联想而来。如下图&#xff0c;我们拥有三个初始xxx值&#xff0c;x1,x2,x0x_1,x_2,x_0x1​,x2​,x0​。其中x01x_01x0​1为一个初始的常量&#xff0c;专业上称作“偏置”。每个xxx的值都会乘上一个权重…...

C++ Primer第五版_第三章习题答案(21~30)

文章目录练习3.21练习3.22练习3.23练习3.24练习3.25练习3.26练习3.27练习3.28练习3.29练习3.30练习3.21 请使用迭代器重做3.3.3节的第一个练习。 #include <vector> #include <iterator> #include <string> #include <iostream>using std::vector; usi…...

colmap+openmvs进行三维重建流程全记录

window下的colmapopenmvs进行三维重建流程全记录 1.colmap安装与配置 可参考&#xff1a;https://blog.csdn.net/weixin_44153180/article/details/129334018?spm1001.2014.3001.5501 2.openmvs安装与配置 可参考&#xff1a;https://blog.csdn.net/rdw1246010462/article…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)

第一篇&#xff1a;Liunx环境下搭建PaddlePaddle 3.0基础环境&#xff08;Liunx Centos8.5安装Python3.10pip3.10&#xff09; 一&#xff1a;前言二&#xff1a;安装编译依赖二&#xff1a;安装Python3.10三&#xff1a;安装PIP3.10四&#xff1a;安装Paddlepaddle基础框架4.1…...

sshd代码修改banner

sshd服务连接之后会收到字符串&#xff1a; SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢&#xff1f; 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头&#xff0c…...

Easy Excel

Easy Excel 一、依赖引入二、基本使用1. 定义实体类&#xff08;导入/导出共用&#xff09;2. 写 Excel3. 读 Excel 三、常用注解说明&#xff08;完整列表&#xff09;四、进阶&#xff1a;自定义转换器&#xff08;Converter&#xff09; 其它自定义转换器没生效 Easy Excel在…...

PostgreSQL 与 SQL 基础:为 Fast API 打下数据基础

在构建任何动态、数据驱动的Web API时&#xff0c;一个稳定高效的数据存储方案是不可或缺的。对于使用Python FastAPI的开发者来说&#xff0c;深入理解关系型数据库的工作原理、掌握SQL这门与数据库“对话”的语言&#xff0c;以及学会如何在Python中操作数据库&#xff0c;是…...