Jetpack:Room超详细使用踩坑指南!,移动终端软件开发课本

/**

  • 自定义工厂,可传入Dao参数

*/

class MyViewModelFactory(private val dao: SimpleStudentDao) : ViewModelProvider.Factory {

override fun <T : ViewModel?> create(modelClass: Class): T {

if (modelClass.isAssignableFrom(SimpleViewModel::class.java)) {

@Suppress(“UNCHECKED_CAST”)

return SimpleViewModel(dao) as T

}

throw IllegalArgumentException(“Unknown ViewModel class.”)

}

}

Activity

class SimpleRoomDemoActivity : AppCompatActivity() {

/**

  • binding

*/

private var _binding: ActivitySimpleUseRoomBinding? = null

private val binding get() = _binding!!

/**

  • 数据库dao

*/

private val simpleDao: SimpleStudentDao by lazy(LazyThreadSafetyMode.NONE) {

SimpleMyDataBase.getDataBase().simpleStudentDao()

}

/**

  • viewModel

*/

lateinit var viewModel: SimpleViewModel

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

_binding = ActivitySimpleUseRoomBinding.inflate(layoutInflater)

setContentView(binding.root)

initParam()

initView()

}

private fun initParam() {

viewModel = ViewModelProvider(

this,

//传入自己的工厂

MyViewModelFactory(simpleDao)

)[SimpleViewModel::class.java]

}

private fun initView() {

with(binding) {

btnInsert.setOnClickListener {

viewModel.insertStudent(SimpleStudentEntity(0, “zxf”, “18”))

}

btnInsertAll.setOnClickListener {

viewModel.insertStudentAll(

arrayListOf(

SimpleStudentEntity(0, “liSi”, “18”),

SimpleStudentEntity(0, “wangWu”, “18”)

)

)

}

//delete 和 update 是根据什么来的 看了源码生成的sql默认根据主键来的

btnDelete.setOnClickListener {

viewModel.deleteStudent(SimpleStudentEntity(2, “delete”, “99”))

// viewModel.deleteStudent(SimpleStudentEntity(199,“update”,“99”))

}

btnUpdate.setOnClickListener {

//所以我们这里面可以直接写一个默认的id去设置,不需要非要拿到查询的对象

// viewModel.updateStudent(SimpleStudentEntity(1,“update”,“99”))

viewModel.updateStudent(SimpleStudentEntity(199, “update”, “99”))

}

//看了一下查询生成的源代码,对于对象来说直接new了,所以不会返回null,但是对象的值,没有就是null,需要声明为可null类型

btnGetId.setOnClickListener {

lifecycleScope.launch {

displayToTextView(viewModel.getStudentById(5))

}

}

btnGetAll.setOnClickListener {

lifecycleScope.launch {

displayToTextView(viewModel.getStudentAll())

}

}

}

}

private fun displayToTextView(students: List) {

val string = students.joinToString(

“”"

“”".trimIndent()

)

binding.text.text = string

}

/**

  • 释放binding

*/

override fun onDestroy() {

super.onDestroy()

_binding = null

}

}

结果


在这里插入图片描述

三、Room踩坑解答

=============================================================================

1.为什么不可以在主线程,进行数据库操作呢?如果非要使用会发生什么?

​ 关于这一点我起初猜想应该和LiveData一样在方法执行的时候,首先对线程做了判断。比如LiveData的setVaule方法,在第一步就判断了,如果不是主线程则抛异常。这个应该也差不多。Jetpack:LiveData使用指南,实现原理详细解析!

​ 我们在生成的Dao实现类里面找一下代码,比如上方我们的Dao接口叫做SimpleStudentDao,通过注解处理器生成了SimpleStudentDao_Impl类,随便找一个方法,比如insert吧,看一下源码:

public void insertStudent(final SimpleStudentEntity studentEntity) {

__db.assertNotSuspendingTransaction();

__db.beginTransaction();

try {

__insertionAdapterOfSimpleStudentEntity.insert(studentEntity);

__db.setTransactionSuccessful();

} finally {

__db.endTransaction();

}

}

​ 第一个assertNotSuspendingTransaction验证阻塞函数是否处于正确的作用域中(kotlin协程+room造成的事务问题,后面会有文章介绍到,持续关注哈!),如果没有这个验证的话会造成死锁。

​ 看第二个beginTransaction干了什么吧

public void beginTransaction() {

assertNotMainThread();

}

public void assertNotMainThread() {

if (isMainThread()) {

throw new IllegalStateException(“Cannot access database on the main thread since it may potentially lock the UI for a long period of time.”);

}

}

所以不可能在主线程进行数据库操作,否则直接就抛出异常😥。

2.看一下查询的方法,如果没有查询到数据会返回什么?空指针吗?还是什么,声明字段需要注意什么?

上面的代码都是用kotlin写的,我们知道kotlin有非空和可空两种类型,那如果没有查询到,**Room返回的是null的话,就需要在将声明的字段或者返回类型声明为可空类型了,不然将null赋值给kotlin的非空类型会抛出异常的!**所以我们需要知道,没有查询到结果的话会返回什么,我们继续看源码,看一下查询的方法。

public List getStudentAll() {

final String _sql = “select * from simple_student”;

final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);

__db.assertNotSuspendingTransaction();

final Cursor _cursor = DBUtil.query(__db, _statement, false, null);

try {

final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, “student_id”);

final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, “student_name”);

final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, “student_age”);

final List _result = new ArrayList(_cursor.getCount());

while(_cursor.moveToNext()) {

final SimpleStudentEntity _item;

final int _tmpId;

_tmpId = _cursor.getInt(_cursorIndexOfId);

final String _tmpName;

if (_cursor.isNull(_cursorIndexOfName)) {

_tmpName = null;

} else {

_tmpName = _cursor.getString(_cursorIndexOfName);

}

final String _tmpAge;

if (_cursor.isNull(_cursorIndexOfAge)) {

_tmpAge = null;

} else {

_tmpAge = _cursor.getString(_cursorIndexOfAge);

}

_item = new SimpleStudentEntity(_tmpId,_tmpName,_tmpAge);

_result.add(_item);

}

return _result;

} finally {

_cursor.close();

_statement.release();

}

}

很明显的看到,即使一个数据也查不到,也会返回一个List对象,所以返回的类型不需要声明为可空类型,对于实体类,也是提前创建好的对象,将查到的数据赋值进去,**但是引用类型如果没有查到,则字段会赋值为空。所以在创建Entity的时候,需要将字段声明为可空类型!**如上面的实体类声明所示:

3.对于update和delete方法,是通过那个字段判断的,Dao里面接口的代码写道需要传入一个Entity类,难道需要先查询,在进行delete和update的操作?

同样,看源码。就看delete吧

public void deleteStudent(final SimpleStudentEntity studentEntity) {

try {

__deletionAdapterOfSimpleStudentEntity.handle(studentEntity);

__db.setTransactionSuccessful();

}

}

this.__deletionAdapterOfSimpleStudentEntity = new EntityDeletionOrUpdateAdapter(__db) {

@Override

public String createQuery() {

return “DELETE FROM simple_student WHERE student_id = ?”;

}

@Override

public void bind(SupportSQLiteStatement stmt, SimpleStudentEntity value) {

stmt.bindLong(1, value.getId());

}

};

很明显可以看到一个sql语句“DELETE FROM simple_student WHERE student_id = ?”;
update在同样的地方sql语句为“UPDATE OR ABORT simple_student SET student_id = ?,student_name = ?,student_age = ? WHERE student_id = ?”。
所以在默认的情况下,是根据主键(上面设置的PrimaryKey)来进行判断,对于delete和update,只需要知道对应的主键就可以进行操作了,并不需要专门先查询出对应的对象,然后在进行对应的操作(之前在别的博客看到的🤣),比如上面的代码就是直接写死的id,新创建的对象进行操作的!

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

学习分享

在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了

很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

2021最新上万页的大厂面试真题

七大模块学习资料:如NDK模块开发、Android框架体系架构…

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
第三,到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。如果你有需要,我这里恰好有为什么,不来领取!说不定能改变你现在的状态呢!
由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

[外链图片转存中…(img-wKgZCaEJ-1712738855323)]

七大模块学习资料:如NDK模块开发、Android框架体系架构…

[外链图片转存中…(img-KcaBpU8F-1712738855324)]

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
第三,到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。如果你有需要,我这里恰好有为什么,不来领取!说不定能改变你现在的状态呢!
由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-T77jW5JC-1712738855324)]

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值