Jetpack Room first try
Jetpack Room is an official Android framework for manipulating the App’s SQLite database. The overall use is relatively simple.
Write a simple Hello World for managing users.
The environment
Android Studio 4.2 Kotlin 1.4.31 Kotlin Coroutines 1.5.0 Jetpack Room 2.3.0
- Depend on the add
def room_version = "2.3.0"
/ / core library
implementation "androidx.room:room-runtime:$room_version"
// Use kapt(Kotlin Annotation Processing tool) to process annotations
kapt "androidx.room:room-compiler:$room_version"
// Use KSP (Kotlin Symbolic Processing) to process annotations (faster)
ksp("androidx.room:room-compiler:$room_version")
// Room coroutine support
implementation("androidx.room:room-ktx:$room_version")
// Room RxJava2 support
implementation "androidx.room:room-rxjava2:$room_version"
// Room RxJava3 support
implementation "androidx.room:room-rxjava3:$room_version"
// Room Guava support
implementation "androidx.room:room-guava:$room_version"
/ / test Room
testImplementation("androidx.room:room-testing:$room_version")
/ / coroutines
def coroutine_version = "1.5.0"
// Coroutine core library
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
// Provide an Android-specific scheduler
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
Copy the code
Why import the coroutine library? Since Room stipulates that database operations cannot be performed on the main thread, coroutines are introduced to handle this problem. In addition, Room supports coroutines and can be used in combination with them.
Three components of Room
Room consists of three components:
- DataBase
The database holding class, which inherits from the RoomDatabase class, holds zero or more abstract methods to get the DAO
- Entity
A data entity class that defines the structure of a database table.
- DAO
Data manipulation interface, which defines methods for manipulating data.
Hello World
So let’s see how to write Hello World.
First of all, you have to define an entity, let’s call it User.
// Use the @Entity annotation to identify an Entity component
@Entity
data class User(
// Primary key, autoGenerate=true
@PrimaryKey(autoGenerate = true)
val id: Long = 0./ / column information
@ColumnInfo(name = "name")
val name: String="".@ColumnInfo(name = "gender")
val gender: String="".@ColumnInfo(name = "age")
val age: String=""
)
Copy the code
If you have an entity, you must have a method to manipulate the entity, so define a UserDao
// Identifies a DAO component
@Dao
interface UserDao {
Vararg indicates that multiple users can be inserted at once
/ / onConflict logo insert encounter conflict resolution strategy, optional REPLACE, the ROLLBACK and ABORD, FAIL, IGNORE, simple and clear, the words can understand
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertUser(vararg users: User)
// Delete the user's annotations
@Delete
fun deleteUser(user: User)
// Delete all user annotations. Use the Query annotation to execute custom SQL statements
@Query("DELETE FROM user")
fun deleteAll(a)
// Update the user's annotations. You can also set the conflict policy
@Update(onConflict = OnConflictStrategy.REPLACE)
fun updateUser(user: User)
// Query all users
@Query("SELECT * FROM user")
fun queryAllUser(a): List<User>
// Query the user by name. :name corresponds to the name parameter in the method
@Query("SELECT * FROM USER WHERE name=:name")
fun queryByName(name: String): User?
}
Copy the code
With DAO, how to use DAO methods to operate the database, also need to define a UserDataBase abstract class
// @database identifies a DataBase component. Entities can be passed as entities owned by the DataBase. Version is used to update the DataBase version
@Database(entities =[User::class,],version = 1)
abstract class UserDataBase :RoomDatabase() {// Define an abstract method to retrieve the UserDao defined above
abstract fun userDao(a):UserDao
}
Copy the code
Now that you have all three components together, it’s time to actually get started.
First, you need to construct a UserDataBase object using Room
private val dataBase: UserDataBase by lazy {
// Pass in the context, the UserDataBase, and the name of the database. DatabaseBuilder can also set other things, such as importing the database from a file, timeout, and so on.
Room.databaseBuilder(this, UserDataBase::class.java, "user.db").build()
}
Copy the code
Since Hell World is only used in MainActivity, use the singleton lazy mode to create a Hell World in MainActivity. Normally, use the singleton lazy mode to create a Hell World, because the frequent creation of a DataBase is expensive.
With the dataBase object, you can directly access the UserDao to manipulate the data, starting with the simplest additions, deleters, modifications and queries. First write a way to create users:
private fun createUser(a): User {
val name = binding.etName.text.toString()
val gender = binding.etGender.text.toString()
val age = binding.etAge.text.toString()
return User(name = name, gender = gender, age = age)
}
Copy the code
Simply read the data in the input box and construct a user, using a ViewBinding to retrieve the component.
- increase
binding.btnInsert.setOnClickListener {
launch {
// The database cannot be operated on the main thread, so the IO thread is used here
withContext(Dispatchers.IO) {
dataBase.userDao().insertUser(createUser())
}
// Update the UI after insertion
updateView()
Toast.makeText(this@MainActivity."Insert success", Toast.LENGTH_SHORT).show()
}
}
Copy the code
- delete
binding.btnDelete.setOnClickListener {
launch {
withContext(Dispatchers.IO) {
dataBase.userDao().deleteUser(createUser().copy(id=getUid()))
}
updateView()
Toast.makeText(this@MainActivity."Delete success", Toast.LENGTH_SHORT).show()
}
}
Copy the code
- Delete all
binding.btnDelete.setOnLongClickListener {
launch {
withContext(Dispatchers.IO) {
dataBase.userDao().deleteAll()
}
updateView()
Toast.makeText(this@MainActivity."Delete all success", Toast.LENGTH_SHORT).show()
}
true
}
Copy the code
- change
// Get the UID in the input box
private fun getUid(a): Long {
return try {
binding.etId.text.toString().toLong()
} catch (e: Exception) {
0L
}
}
binding.btnUpdate.setOnClickListener {
launch {
// Create a new User using the copy method and set its ID
withContext(Dispatchers.IO) {
dataBase.userDao().updateUser(createUser().copy(id=getUid()))
}
updateView()
Toast.makeText(this@MainActivity."Update success", Toast.LENGTH_SHORT).show()
}
}
Copy the code
- check
binding.btnQuery.setOnClickListener {
launch {
// Query the user by name. WithContext returns the last line, so you can get the user information here
valuser = withContext(Dispatchers.IO) { dataBase.userDao().queryByName(binding.etName.text.toString()) } user? .let { binding.tvInfo.text ="${it.id}-${it.name} ${it.gender} ${it.age}"
}
Toast.makeText(this@MainActivity."Query success", Toast.LENGTH_SHORT).show()
}
}
Copy the code
So, if it’s super simple, some of Jetpack’s frameworks are really good for lazy people. Finally put the overall source code:
- MainACtivity
package com.ricky.room
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.room.Room
import com.ricky.room.databinding.ActivityMainBinding
import kotlinx.coroutines.*
import java.lang.Exception
class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
private val binding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }
private val dataBase: UserDataBase by lazy {
Room.databaseBuilder(this, UserDataBase::class.java, "user.db").build()
}
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.btnInsert.setOnClickListener {
launch {
withContext(Dispatchers.IO) {
dataBase.userDao().insertUser(createUser())
}
updateView()
Toast.makeText(this@MainActivity."Insert success". Toast.LENGTH_SHORT).show() } } binding.btnDelete.setOnClickListener { launch { withContext(Dispatchers.IO) { dataBase.userDao().deleteUser(createUser().copy(id=getUid())) } updateView() Toast.makeText(this@MainActivity."Delete success". Toast.LENGTH_SHORT).show() } } binding.btnDelete.setOnLongClickListener { launch { withContext(Dispatchers.IO) { dataBase.userDao().deleteAll() } updateView() Toast.makeText(this@MainActivity."Delete all success", Toast.LENGTH_SHORT).show()
}
true
}
binding.btnUpdate.setOnClickListener {
launch {
withContext(Dispatchers.IO) {
dataBase.userDao().updateUser(createUser().copy(id=getUid()))
}
updateView()
Toast.makeText(this@MainActivity."Update success", Toast.LENGTH_SHORT).show()
}
}
binding.btnQuery.setOnClickListener {
launch {
valuser = withContext(Dispatchers.IO) { dataBase.userDao().queryByName(binding.etName.text.toString()) } user? .let { binding.tvInfo.text ="${it.id}-${it.name} ${it.gender} ${it.age}"
}
Toast.makeText(this@MainActivity."Query success", Toast.LENGTH_SHORT).show()
}
}
updateView()
}
private fun getUid(a): Long {
return try {
binding.etId.text.toString().toLong()
} catch (e: Exception) {
0L}}private fun createUser(a): User {
val name = binding.etName.text.toString()
val gender = binding.etGender.text.toString()
val age = binding.etAge.text.toString()
return User(name = name, gender = gender, age = age)
}
private fun updateView(a) {
launch {
val text = withContext(Dispatchers.IO) {
val users = dataBase.userDao().queryAllUser()
users.joinToString("\n") { "${it.id}-${it.name} ${it.gender} ${it.age}" }
}
binding.tvInfo.text = text
}
}
}
Copy the code
- activity_main.xml
<? xml version="1.0" encoding="utf-8"? > <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/guideline2"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="435dp" />
<EditText
android:id="@+id/et_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:hint="ID"
app:layout_constraintEnd_toStartOf="@id/et_name"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/guideline2" />
<EditText
android:id="@+id/et_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:hint="Name"
app:layout_constraintEnd_toStartOf="@id/et_gender"
app:layout_constraintStart_toEndOf="@id/et_id"
app:layout_constraintTop_toBottomOf="@id/guideline2" />
<EditText
android:id="@+id/et_gender"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:hint="Gender"
app:layout_constraintEnd_toStartOf="@id/et_age"
app:layout_constraintStart_toEndOf="@id/et_name"
app:layout_constraintTop_toBottomOf="@id/guideline2" />
<EditText
android:id="@+id/et_age"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:hint="Age"
android:inputType="number"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/et_gender"
app:layout_constraintTop_toBottomOf="@id/guideline2" />
<Button
android:id="@+id/btn_insert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="insert"
app:layout_constraintBottom_toTopOf="@+id/btn_update"
app:layout_constraintEnd_toStartOf="@id/btn_delete"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline2" />
<Button
android:id="@+id/btn_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="delete"
app:layout_constraintBottom_toTopOf="@id/btn_query"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/btn_insert"
app:layout_constraintTop_toBottomOf="@id/guideline2" />
<Button
android:id="@+id/btn_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="14dp"
android:text="update"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/btn_query"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_insert" />
<Button
android:id="@+id/btn_query"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="query"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/btn_update"
app:layout_constraintTop_toBottomOf="@id/btn_delete" />
</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code