介绍
与网络一样,在开发 Android 应用程序时,使用数据库同样频繁。在某种程度上,这两个领域是交织在一起的,因为数据库通常存储从网络检索到的数据,现在需要缓存以减少对网络的请求数量。使用具有几十行的简单表时,您不会遇到任何问题。
但是,在我的实践中,有一种情况是您必须加载具有数百个表的数十个数据库,其中一些包含大约一百万行。我也遇到过通过嵌套SELECT链接表的情况,嵌套级别高达 5-10。为了解决所有这些问题,我不得不使用下面列出的所有库和工具来获得所需的结果或修复错误。因为有时一种工具不允许我做另一种工具允许我做的事情。
观察和假设
首先,我想提请您注意一些评论和假设:
- 本文将看具体的例子,其代码可以插入到Android Studio中,运行看看会发生什么;
- 本文将重点介绍使用关系数据库。我们不会考虑非关系型(NoSQL)数据库;
- 我们将考虑数据库sqlite;
- 我们将从简单的事情开始,逐渐增加复杂性;
- 代码不会根据MVP和MVVM模式进行分层。这样做是为了简化示例和整个文章;
- 这篇文章是为中级及以上的开发者设计的,所以我们不会详述非常简单的事情,同样的原因我们不会深入每个单独的示例或库。
仅提供了拓宽视野的工具的概述。
选择一个例子
要查看下面的库和工具,让我们选择一个示例。假设我们有一个包含两列的表:id 和 color。让我们尝试用不同的方式将这张表写入数据库,得到这样的结果:
用于使用 SQLite 数据库的工具和库
SQLiteOpenHelper
让我们看一下第一个在Android上使用数据库的原生工具SQLiteOpenHelper。我们的任务是将数据写入设备上的数据库。
您不需要引入任何库。创建一个Activity,插入以下代码并运行它。
不要忘记将其设置为后台线程,如图:
package com.leonidivankin.draftsandroid.articles.db import android.content.ContentValues import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity class SQLiteOpenHelperActivity : AppCompatActivity() { private val db by lazy { object : SQLiteOpenHelper(this, "colors_db", null, 1) { override fun onCreate(db: SQLiteDatabase) { db.execSQL("CREATE TABLE $TABLE_NAME (_id INTEGER PRIMARY KEY AUTOINCREMENT, color TEXT)") } override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {} }.readableDatabase } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Thread{ insert( "green") insert( "red") insert( "blue") get() }.start() } private fun insert(color: String) { val contentValues = ContentValues() contentValues.put(COLUMN_NAME, color) db.insert(TABLE_NAME, null, contentValues) } private fun get() { val cursor = db.query(TABLE_NAME, arrayOf("_id", COLUMN_NAME), null, null, null, null, null) var i = 0 while (!cursor.isLast) { cursor.moveToPosition(i) val id = cursor.getInt(0) val color = cursor.getString(1) Log.d("DbExample", "$id $color") i++ } } override fun onDestroy() { super.onDestroy() db.close() } companion object { const val TABLE_NAME = "colors" const val COLUMN_NAME = "color" } }
我们创建了一个colors_db数据库。
输出日志显示我们实现了目标:
D: 1 green D: 2 red D: 3 blue
如您所见,创建表格、填充数据并输出表格只用了不到 50 行代码。
优点和缺点
- +不需要额外的库
- – 很难处理复杂的查询和选择
- -每次都需要映射到ContentValues、Cursor并返回
有用的链接: https ://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper https://www.sqlite.org/index.html
它不处理错误、更改数据库版本的方法等以减少代码。
接下来,我们将需要一个带有此表的示例。
房间
现在在 Google 的Room的帮助下制作同一张桌子,它是Android Jetpack的一部分。实际上,Room是一个ORM,即一个界面更友好的外壳。
添加对库的依赖:
implementation 'androidx.room:room-runtime:2.1.0' kapt 'androidx.room:room-compiler:2.1.0'
向kapt build.gradle模块级文件 添加依赖项:apply plugin: 'kotlin-kapt'
创建一个Activity,插入代码并运行它,如图所示:
package com.leonidivankin.draftsandroid.articles.db import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity import androidx.room.* import androidx.room.OnConflictStrategy.REPLACE class RoomActivity : AppCompatActivity() { private val colorDao by lazy { Room .databaseBuilder(applicationContext, AppDatabase::class.java, "colors_room") .build() .colorDao() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Thread { val list = listOf(Color(1, "green"), Color(2, "red"), Color(3, "blue")) colorDao.insert(list) Log.d("DbExample", "${colorDao.get()}") }.start() } } @Database(entities = [Color::class], version = 1, exportSchema = false) abstract class AppDatabase : RoomDatabase() { abstract fun colorDao(): ColorDao } @Entity(tableName = "colors") data class Color(@PrimaryKey val id: Int, val color: String) @Dao interface ColorDao { @Query("SELECT * FROM colors") fun get(): List<Color> @Insert(onConflict = REPLACE) fun insert(color: List<Color>) }
我们创建了colors_room基础。
在这里,我们也很快达到了我们的目标,正如日志所证实的那样:
D: [Color(id=1, color=green), Color(id=2, color=red), Color(id=3, color=blue)]
优点和缺点
- +简化复杂查询的创建
- +可以给对象RxJava , LiveData , Flow
- +适用于对象,而不是字符串
- +可以一次给出一个对象列表
- +它有自动迁移
- – 像任何库一样,它使项目更重
有用的链接:
https://developer.android.com/jetpack/androidx/releases/room
我们在 30-40 行中完成了最初设定的任务。如您所知,Room是目前最流行的用于在 Android 上处理数据库的库,因为它简单且速度快。
其他 ORM
实际上,Room库是一个ORM。它的主要任务是简化SQLite数据库的工作。在Android上,之前还有其他流行的用于处理数据库的 ORM,例如ORMLite、SugarORM、Freezer等。
他们有十几个。在Room发布后,它们失去了人气,特别是考虑到 Room 比其他ORM更快这一事实,正如这样的基准所证明的那样。
我们对数据库的讨论到此结束。接下来我们来看看检查工具。
数据库检查
设备文件资源管理器 + SQLite Studio
整个SQLite数据库存储在 Android 上的Internal Storage中的一个或多个文件中。无需增加额外的服务器或连接来使用它。因此,查看生成文件中内容的最简单方法是将其下载到您的计算机(它是免费的)并在SQLite Studio编辑器中打开它。这就是我们要做的。
转到以下路径:设备文件资源管理器 > 数据 > 数据 > your_package_name > 数据库并找到之前创建的数据库colors_db和colors_room:
选择这些文件并将它们保存到您的计算机:
接下来,下载SQLite Studio https://sqlitestudio.pl/并安装它。
打开SQLite Studio,找到我们的文件,如下图:
结果,数据库应该出现在左侧的窗口中。双击打开所需的数据库和表,它应该出现在右侧的窗口中:
除了查看数据库,您还可以更改数据并将其上传回您的移动设备。这就是我们要做的。让我们将绿色更改为紫色并保存为示例:
然后返回设备文件资源管理器并通过单击上传按钮上传新文件:
有时您需要重新启动应用程序才能更新数据。转到RoomActivity,注释掉一些代码来编写并重新启动应用程序:
//val list = listOf(Color(1, "green"), Color(2, "red"), Color(3, "blue")) //colorDao.insert(list)
请参阅LogCat:
D: [Color(id=1, color=purple), Color(id=2, color=red), Color(id=3, color=blue)]
我们看到第一个Color对象的颜色值已经变成了紫色。这为您提供了一个很好的工具包,用于在开发、调试和纠错期间管理基础。
优点和缺点
- +您可以记录数据
- – 你每次都必须下载和上传数据库
有用的链接:
https://www.sqlite.org/index.html
我们分析了检查数据库的最简单、最可靠的方法之一。让我们再看几个。
Android Studio 数据库检查器
上例所示的方法长期以来一直是使用 GUI 查看数据库的主要方式。但是,Android Studio 4.1引入了Database Inspector。
要使用它,您需要:
- 转到应用检查选项卡;
- 选择您的申请流程(在我的情况下是
com.leonidivankin.draftandroid
); - 找到你的数据库(在我的例子中是colors_room);
- 找到必要的表格(在我的例子中是colors)。
如您所见,数据是相同的。在此工具中,还可以编辑值。在该行上单击两次并输入所需的值,例如,我将输入rose:
重新启动应用程序并查看LogCat:
D: [Color(id=1, color=rose), Color(id=2, color=red), Color(id=3, color=blue)]
如您所见,日志中的值更改为rose。
我想指出的是,在Android Studio 4.1中,该工具的工作相当糟糕:并非总是可以连接到必要的进程,有时会意外丢失与数据库的连接,但质量和功能正在不断改进。
在Android Studio Chipmunk 2021.2.1 中, Database Inspector工作得更好,尽管一些问题仍然存在。
优点和缺点
- +无需复制基础并重新填写
- -原始工具
我们看到了如何在不离开Android Studio的情况下查看SQLite数据库。接下来,让我们看看如何通过ADB进行操作。
亚行+sqlite
有时 GUI 功能是不够的,例如,如果您想创建一个新的空数据库。或者,如果您想编写一个包含一系列操作的脚本以进行测试。adb+sqlite工具会有所帮助。
要使用它,请在模拟器 (!) 上运行应用程序并输入以下行:
adb shell su cd data/data/com.leonidivankin.draftsandroid/databases sqlite3 colors_room SELECT * FROM colors;
其中,com.leonidivankin.draftsandroid
– 我的包的名称,colors_room,colors – 上面示例中的基础和表格的名称。
我们在控制台中看到显示了表格的内容,如前面的示例所示:
几点观察:
- 必须在模拟器上运行应用程序,否则会出现Permission denied或的错误
su: not found
,因为你需要 root 手机; - 必须使用分号“;” 否则,
SELECT * FROM colors;
可能会导致错误或无法执行查询。
您还可以查看所有数据库的列表。为此,您必须输入:
ls -R /data/data/com.leonidivankin.draftsandroid/databases
结果,我们将所有文件都包含在数据库文件夹中:
优点和缺点
- +与图形命令相比,可用的命令范围非常广泛
- +可以为自动化或自测编写脚本
- – 你必须通过控制台输入命令
- – 需要有根电话或模拟器
有用的链接: https ://developer.android.com/studio/command-line/adb
尽管Adb+sqlite工具没有图形界面,但值得关注,因为它更灵活,为开发或测试的自动化提供了越来越多的机会,例如正确处理迁移。
结论
使用包含数十个表和数千行的 SQLite 数据库是一项艰巨的任务。很多时候,发现错误需要所有可能的武器库,这些武器库仅在Android上可用。上面介绍的工具将帮助您解决这些问题。
Android从一开始就一直在使用SQLite数据库。即使其他一些工具每年都在变化,SQLite仍然是第二个十年的主要存储库之一。而且,无论如何,它将保持这种状态。
这就是为什么我们在这里提供的建议永远不会失去其相关性。如果您使用本文未提及的任何工具,请在评论中分享。