在Android中使用Realm数据库
Realm是一个开源的、开发者友好的、轻量级的移动数据库。它可以成为SQLite和CoreData的良好替代品。
Realm速度更快,而且有大量的新功能,如支持JSON、易于使用的API、数据变化时的通知、跨平台支持、更快的查询,而且它是完全加密的,所有这些都使移动开发变得简单。
在本教程中,我们将探讨Realm for Android的基本原理。
前提条件
要完成本教程,你应该有。
- 在你的机器上安装了[Android Studio]。
- 具有创建和运行Android应用程序的良好知识。
- [Kotlin]编程语言的基本信息。
- 对Android中的[MVVM架构]有良好的理解。
SQLite数据库
轻量级的、开源的、结构化查询基础的、独立的、用于Android设备的离线数据库。它支持嵌入式关系型数据库功能。该数据库存储在设备的本地目录中,数据以文本格式表示。
安卓设备配备了内置的SQLite数据库实现。SQLite是一个关系型数据库,它包含由行和列组成的表、索引和其他许多东西。像SQLite这样的关系型数据库有一个模式,存储了关于、表、关系、触发器、索引等信息。
房间持久性库是一个抽象库,它工作在SQLite的顶部。它允许更多的健壮性和容易利用SQLite的力量。我们的数据库对象被Room库翻译成Java对象。
核心数据
Core data是一个由苹果公司提供的数据持久化库。为支持macOS和iOS操作系统而开发。它将结构化的数据保存在设备的本地。
核心数据管理着一个对象图。对象图是一个相互连接的对象的集合。请记住,CoreData不是一个数据库,它是一个管理复杂对象图的框架。
境界数据库
Realm数据库可以被直接访问,这使得它的速度更快。它存储实时对象,不需要对象关系映射库。
Realm是用C++从头开始建立的,因为它的核心可以直接在不同的设备上运行。它以本地对象格式存储数据,对象在数据库中以基于表格的通用格式表示。这使得Realm数据库易于在不同的语言和平台上使用。
在本教程中,我们将重点介绍Realm数据库在Android中与Kotlin的应用。
为什么使用Realm数据库?
- 迅速建立强大和更好的应用程序
- 它是一个跨平台的数据库
- 在本地使用Realm数据库时的离线模式
- 使用Realm Sync时的在线数据同步
- 简单而快速的查询
通过Realm数据库,我们可以
- 执行对象模式定义
- 从Realm数据库访问数据
- 更新实时对象
- 关注对象的更新
- 始终获得最新的数据
Realm中的交易
Realm 以事务的方式处理reads 和writes 进入数据库的数据。一个事务是一组读和写的操作,它们被当作一个不能分割的单一操作。一个事务要么成功,要么根本就不发生。
为了运行一个事务,我们可以使用executeTransaction() 或executeTransactionAsync() 方法。如果执行失败,事务会被取消,否则,事务会被提交。
在大多数情况下,你应该使用executeTransaction() ,因为它可以处理错误并为你关闭Realm数据库。
在Android中开始使用Realm
我们将创建一个Todo 应用程序来演示CRUD操作,即查询、创建、更新和删除。
第1步:包括Realm的依赖性
打开build.gradle 文件(项目级)并粘贴Realm类路径依赖。
classpath "io.realm:realm-gradle-plugin:10.7.0"
在应用级build.gradle 插件中,添加这两个插件。请确保,从kotlin-kapt 。
plugins {
...
id 'kotlin-kapt'
id 'realm-android'
}
第2步:创建一个基础应用程序类
这个类将包含onCreate() 方法,该方法将在任何其他方法之前被调用一次。在每次启动应用程序时,它将被用于初始化Realm数据库。
class RealmApp : Application() {
override fun onCreate() {
super.onCreate()
Realm.init(this)
val configuration = RealmConfiguration.Builder()
.name("todo.db")
.deleteRealmIfMigrationNeeded()
.schemaVersion(0)
.allowWritesOnUiThread(true)
.allowQueriesOnUiThread(true)
.build()
Realm.setDefaultConfiguration(configuration)
}
}
代码解释
初始化Realm -Realm.init(this)
在你的项目中利用Realm库之前,你必须首先初始化它。每次你的应用程序运行时,它应该只初始化Realm一次。
打开和配置Realm
使用RealmConfiguration来控制你想打开的Realm的具体内容。
val configuration = RealmConfiguration.Builder()
.name("todo.db")
.deleteRealmIfMigrationNeeded()
.schemaVersion(0)
.allowWritesOnUiThread(true)
.allowQueriesOnUiThread(true)
.build()
Realm.setDefaultConfiguration(configuration) - 将此配置设置为默认的Realm。
在配置你的Realm时,使用readOnly() 方法,使其成为只读。
注意:总是记得在完成一个境界实例时调用
realm.close()方法来释放资源。忽视关闭Realm会导致OutOfMemoryError。
第3步:在Android Manifest中注册你的应用程序子类
为了执行我们的自定义应用代码,你必须将你的应用子类包含到AndroidManifest.xml 文件中。设置清单中应用程序定义的android.name 属性,以确保当用户运行应用程序时,Android会在任何其他类之前实例化应用程序子类。
<application
android:name=".MyApplicationSubclass"
...
/>
第4步:定义你的对象模型
这个类定义了我们的数据将被存储在数据库中的结构。
import io.realm.RealmModel
import io.realm.annotations.PrimaryKey
import io.realm.annotations.RealmClass
import io.realm.annotations.Required
@RealmClass
open class Note : RealmModel {
@PrimaryKey
var id: String = ""
@Required
var title: String? = ""
@Required
var description: String? = ""
}
Realm对象应该继承自RealmModel 类,提供一个空的构造函数并使用open 可见性修改器。
Realm模型的属性可以有注释,如@PrimaryKey ,以定义一个唯一的属性,@Required ,以定义一个必须包括的领域。
第5步:创建ViewModel类
这个生命周期感知类将包含我们所有的CRUD操作功能,即创建、更新和删除。
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import io.realm.Realm
import io.realm.kotlin.deleteFromRealm
import java.util.*
class MainViewModel : ViewModel() {
private var realm: Realm = Realm.getDefaultInstance()
val allNotes: LiveData<List<Note>>
get() = getAllNotes()
fun addNote(noteTitle: String, noteDescription: String) {
realm.executeTransaction { r: Realm ->
val note = r.createObject(Note::class.java, UUID.randomUUID().toString())
note.title = noteTitle
note.description = noteDescription
realm.insertOrUpdate(note)
}
}
private fun getAllNotes(): MutableLiveData<List<Note>> {
val list = MutableLiveData<List<Note>>()
val notes = realm.where(Note::class.java).findAll()
list.value = notes?.subList(0, notes.size)
return list
}
fun updateNote(id: String, noteTitle: String, noteDesc: String) {
val target = realm.where(Note::class.java)
.equalTo("id", id)
.findFirst()
realm.executeTransaction {
target?.title = noteTitle
target?.description = noteDesc
realm.insertOrUpdate(target)
}
}
fun deleteNote(id: String) {
val notes = realm.where(Note::class.java)
.equalTo("id", id)
.findFirst()
realm.executeTransaction {
notes!!.deleteFromRealm()
}
}
fun deleteAllNotes() {
realm.executeTransaction { r: Realm ->
r.delete(Note::class.java)
}
}
}
解释
var realm: Realm = Realm.getDefaultInstance() - 术语 "Realm "以默认配置实例化了 "Realm"。一旦你打开了一个境界,你可以使用写事务块来改变其中的项目。
将数据添加到Realm中
fun addNote(noteTitle: String, noteDescription: String) {
realm.executeTransaction { r: Realm ->
val note = r.createObject(Note::class.java, UUID.randomUUID().toString())
note.title = noteTitle
note.description = noteDescription
realm.insertOrUpdate(note)
}
}
当向 Realm 中添加数据对象时,我们在executeTransaction 方法中执行一个事务。
通过使用r.createObject(ObjectClass, PrimaryKey) 创建一个将被插入数据库的对象,我们把我们的模型类和主键传递给它。
通过我们创建的对象,我们可以通过访问其属性来添加数据。
之后我们调用realm.insertOrUpdate(object) 方法,将新的或更新的数据提交到数据库中。
从Realm数据库中查询数据
为了查询某个特定对象的所有数据,我们创建了一个对象,该对象将存储所有查询的RealmResult。realm.where(Note::class.java).findAll() ,找到所有数据并将其存储在用于存储所有RealmResult的变量中。
然后这个结果可以被转换成一个列表,因为它包含相同类型的数据。
你可以使用这行代码来做到这一点。
list.value = notes?.subList(0, notes.size)
private fun getAllNotes(): MutableLiveData<List<Note>> {
val list = MutableLiveData<List<Note>>()
val notes = realm.where(Note::class.java).findAll()
list.value = notes?.subList(0, notes.size)
return list
}
更新数据
更新数据与我们向realm中插入数据的方式类似,只是这次我们用需要更新的数据的各自id ,查询数据。在我们的查询中,我们将附加.findFirst() 方法来找到要更新的数据的第一个匹配实例。
然后,一旦找到匹配,我们将在executeTransaction() 方法中运行我们的事务,提交更新的数据对象。
fun updateNote(id: String, noteTitle: String, noteDesc: String) {
val target = realm.where(Note::class.java)
.equalTo("id", id)
.findFirst()
realm.executeTransaction {
target?.title = noteTitle
target?.description = noteDesc
realm.insertOrUpdate(target)
}
}
删除一个特定领域对象的单一项目
要从一个境界对象中删除数据,还需要使用id ,查询我们需要删除的特定对象。一旦找到,我们可以使用deleteFromRealm() 方法来删除executeTransaction() 块中的被查询的数据。
fun deleteNote(id: String) {
val notes = realm.where(Note::class.java)
.equalTo("id", id)
.findFirst()
realm.executeTransaction {
notes!!.deleteFromRealm()
}
}
删除一个境界对象的所有数据
删除一个特定对象的所有数据是超级简单的。我们调用executeTransaction ,然后用删除整个对象和它的所有数据。r.delete(Note::class.java)
fun deleteAllNotes() {
realm.executeTransaction { r: Realm ->
r.delete(Note::class.java)
}
}
最后,我们就可以在我们的AddNoteActivity.kt 和MainActivity.kt 文件中调用这些函数来执行各自的任务。
第6步:AddNoteActivity
当添加一个新的笔记时,我们可以从ViewModel类中调用这个函数addNote() 。
binding.saveButton.setOnClickListener {
if (binding.titleEditText.text.toString()
.isEmpty() || binding.descriptionEditText.text.toString().isEmpty()
) {
return@setOnClickListener
} else {
viewModel.addNote(
binding.titleEditText.text.toString(),
binding.descriptionEditText.text.toString()
)
startActivity(Intent(this, MainActivity::class.java))
finish()
}
}
第7步:MainActivity
对于查询所有的笔记,我们可以从ViewModel类中调用getAllNotes() 。
viewModel.allNotes.observe(this, { allNotes ->
notesAdapter.submitList(allNotes)
binding.notesRecyclerview.adapter = notesAdapter
})
要删除一个笔记,我们使用了滑动删除ItemTouchHelper,它从ViewModel类中调用deleteNote() 。
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
viewModel.deleteNote(id!!)
Toast.makeText(this@MainActivity, "Note deleted successfully", Toast.LENGTH_SHORT)
.show()
}
要更新一个特定的笔记,我们将显示一个对话框,允许用户对选定的笔记进行修改。
private fun createUpdateDialog(note: Note) {
// It is recommended to use ViewBinding in place of View.findViewById().
val viewGroup = findViewById<ViewGroup>(android.R.id.content)
val dialogView: View =
LayoutInflater.from(this).inflate(R.layout.update_dialog, viewGroup, false)
val builder = AlertDialog.Builder(this)
val titleEdtxt: EditText = dialogView.findViewById(R.id.titleEditText_update)
val descriptionEdtxt: EditText = dialogView.findViewById(R.id.descriptionEditText_update)
titleEdtxt.setText(note.title)
descriptionEdtxt.setText(note.description)
builder.setView(dialogView)
builder.setTitle("Update Note")
// use underscores for unused lambda parameters
builder.setPositiveButton("Update") { _, _ ->
viewModel.updateNote(
note.id,
titleEdtxt.text.toString(),
descriptionEdtxt.text.toString()
)
notesAdapter.notifyDataSetChanged()
}
builder.setNegativeButton("Cancel") { _, _ ->
Toast.makeText(this@MainActivity, "Canceled update", Toast.LENGTH_SHORT).show()
}
builder.show()
}
要删除数据库中的所有笔记,我们可以使用如下所示的菜单项。
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.delete_all, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.deleteAll) {
viewModel.deleteAllNotes()
notesAdapter.notifyDataSetChanged()
return true
}
return super.onOptionsItemSelected(item)
}
这里有一些截图,说明这个应用程序应该是什么样的。



在这个GitHub仓库中查看整个项目。
总结
Realm是传统SQLite数据库的一个很好的替代品。继续探索和尝试Realm的其他功能,如RealmSync。
当启用Realm Sync时,Realm数据库会在后台线程中与MongoDB Realm同步数据。它将本地数据更新推送到MongoDB Realm并拉动远程变化。