如何在Android中使用境界数据库

253 阅读9分钟

在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 以事务的方式处理readswrites 进入数据库的数据。一个事务是一组读和写的操作,它们被当作一个不能分割的单一操作。一个事务要么成功,要么根本就不发生。

为了运行一个事务,我们可以使用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.ktMainActivity.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)
    }

这里有一些截图,说明这个应用程序应该是什么样的。

All Notes

Add Note

Update Note

这个GitHub仓库中查看整个项目。

总结

Realm是传统SQLite数据库的一个很好的替代品。继续探索和尝试Realm的其他功能,如RealmSync。

当启用Realm Sync时,Realm数据库会在后台线程中与MongoDB Realm同步数据。它将本地数据更新推送到MongoDB Realm并拉动远程变化。