Android在KSP中简单使用Room
最近下载了最新版Studio,好多依赖和配置都需要升级,之前使用过room封装数据库工具类,最近在整理ksp相关,于是把room也升级了,简单记录一下升级过程,直接上代码。
1.添加KSP依赖配置:
#KSP
ksp = "2.1.10-1.0.29"
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
2.项目的依赖配置:
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.library).apply(false)
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.ksp).apply(false)
}
3.app的build.gradle配置:
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.ksp)
}
4.添加room配置:
#Room
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
util-codex = { group = "com.blankj", name= "utilcodex", version.ref = "utilcodex" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
[bundles]
room = [
"androidx-room-ktx",
"androidx-room-runtime",
]
5.在build.gradle添加room:
api(libs.bundles.room)
ksp(libs.androidx.room.compiler)
6.room数据库生成文件路径:
6.1 路径配置:
ksp {
arg("room.schemaLocation", "$projectDir/schemas")
}
defaultConfig {
applicationId = "com.example.ksproomdemo"
minSdk = 24
targetSdk = 36
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
ksp {
arg("room.schemaLocation", "$projectDir/schemas")
}
}
6.2. room对应的jvm版本:
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
ksp {
arg("jvmTarget", "17")
}
6.3 生成的数据库文件如下:
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "1599160ffa834f06d3bdbe0680959e41",
"entities": [
{
"tableName": "User",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` TEXT NOT NULL, `name` TEXT NOT NULL, `sex` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sex",
"columnName": "sex",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "email",
"columnName": "email",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1599160ffa834f06d3bdbe0680959e41')"
]
}
}
7.布局文件:
<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_add"
android:layout_width="200dp"
android:layout_height="40dp"
android:text="添加数据"
android:textColor="@color/white"
android:textSize="18sp"
android:gravity="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="20dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@color/design_default_color_primary"/>
<TextView
android:id="@+id/tv_update"
android:layout_width="200dp"
android:layout_height="40dp"
android:text="修改数据"
android:textColor="@color/white"
android:textSize="18sp"
android:gravity="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_add"
android:layout_marginTop="20dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@color/design_default_color_primary"/>
<TextView
android:id="@+id/tv_query"
android:layout_width="200dp"
android:layout_height="40dp"
android:text="查询数据"
android:textColor="@color/white"
android:textSize="18sp"
android:gravity="center"
android:singleLine="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_update"
android:layout_marginTop="20dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@color/design_default_color_primary"/>
<TextView
android:id="@+id/tv_delete"
android:layout_width="200dp"
android:layout_height="40dp"
android:text="删除数据"
android:textColor="@color/white"
android:textSize="18sp"
android:gravity="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_query"
android:layout_marginTop="20dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@color/design_default_color_primary"/>
<TextView
android:id="@+id/tv_delete_users"
android:layout_width="200dp"
android:layout_height="40dp"
android:text="删除多个数据"
android:textColor="@color/white"
android:textSize="18sp"
android:gravity="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_delete"
android:layout_marginTop="20dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@color/design_default_color_primary"/>
<TextView
android:id="@+id/tv_delete_all"
android:layout_width="200dp"
android:layout_height="40dp"
android:text="删除所有数据"
android:textColor="@color/white"
android:textSize="18sp"
android:gravity="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_delete_users"
android:layout_marginTop="20dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@color/design_default_color_primary"/>
</androidx.constraintlayout.widget.ConstraintLayout>
8.数据库工具类RoomUtils:
package com.example.db
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.example.app.RoomApp
import com.example.ksproomdemo.bean.User
import com.example.ksproomdemo.dao.UserDao
/**
* @author: njb
* @date: 2025/3/30 1:25
* @desc: 描述
*/
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
class RoomUtils private constructor(){
private val database: AppDatabase by lazy {
Room.databaseBuilder(
RoomApp.instance.applicationContext,
AppDatabase::class.java, "app-database"
).build()
}
companion object {
@Volatile
private var instance: RoomUtils? = null
fun getInstance(): RoomUtils {
return instance ?: synchronized(this) {
instance ?: RoomUtils().also { instance = it }
}
}
}
private val userRepository: UserRepository by lazy {
UserRepository(database.userDao())
}
suspend fun insertUser(user: User) {
userRepository.insertUser(user)
}
suspend fun updateUser(user: User) {
userRepository.updateUser(user)
}
suspend fun deleteUser(user: User) {
userRepository.deleteUser(user)
}
suspend fun getAllUsers(): List<User> {
return userRepository.getAllUsers()
}
suspend fun deleteUsers(user: List<User>) {
return userRepository.deleteUsers(user)
}
suspend fun deleteAllUser() {
userRepository.deleteAllUser()
}
}
9.UserRepository:
package com.example.db
import com.example.ksproomdemo.bean.User
import com.example.ksproomdemo.dao.UserDao
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
/**
* @author: njb
* @date: 2025/3/30 1:26
* @desc: 描述
*/
class UserRepository (private val userDao: UserDao){
suspend fun insertUser(user: User) {
withContext(Dispatchers.IO) {
userDao.insertAll(user)
}
}
suspend fun updateUser(user: User) {
withContext(Dispatchers.IO) {
userDao.updateUser(user)
}
}
suspend fun deleteUser(user: User) {
withContext(Dispatchers.IO) {
userDao.delete(user)
}
}
suspend fun getAllUsers(): List<User> {
return withContext(Dispatchers.IO) {
userDao.getAll()
}
}
suspend fun deleteUsers(user: List<User>) {
withContext(Dispatchers.IO) {
userDao.deleteUsers(user)
}
}
suspend fun deleteAllUser() {
withContext(Dispatchers.IO) {
userDao.deleteAllUserInfo()
}
}
}
10.Dao和实体类:
package com.example.ksproomdemo.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.ksproomdemo.bean.User
/**
* @author: njb
* @date: 2025/3/30 1:06
* @desc: 描述
*/
@Dao
interface UserDao {
@Query("SELECT * FROM user")
suspend fun getAll(): List<User>
@Query("SELECT * FROM user WHERE id IN (:userIds)")
suspend fun loadAllByIds(userIds: IntArray): List<User>
@Query("SELECT * FROM user WHERE name LIKE :name")
suspend fun findByName(name: String): User?
@Query("SELECT *FROM user WHERE id LIKE:userId")
suspend fun findById(userId: Int): User?
@Update
suspend fun updateUser(user: User)
@Insert
suspend fun insertAll(users: User)
@Delete
suspend fun delete(user: User)
@Delete
suspend fun deleteUsers(user: List<User>)
@Query("DELETE FROM User")
suspend fun deleteAllUserInfo()
}
package com.example.ksproomdemo.bean
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
/**
* @author: njb
* @date: 2025/3/30 1:24
* @desc: 描述
*/
@Entity(tableName = "User")
data class User(
var userId: String = "",
@ColumnInfo(name = "name") var name: String = "",
@ColumnInfo(name = "sex") var sex: String = "",
@ColumnInfo(name = "email") var email: String = "",
){
@PrimaryKey(autoGenerate = true)
var id: Long = 0
}
11.测试代码:
package com.example.ksproomdemo
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.lifecycleScope
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.SnackbarUtils
import com.blankj.utilcode.util.ToastUtils
import com.example.db.RoomUtils
import com.example.ksproomdemo.bean.User
import com.example.ksproomdemo.databinding.ActivityMainBinding
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class MainActivity : AppCompatActivity() {
private lateinit var roomUtils: RoomUtils
private lateinit var binding:ActivityMainBinding
private val TAG = "Mainivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
initView()
initListener()
}
private fun initView() {
roomUtils = RoomUtils.getInstance()
}
private fun initListener() {
binding.tvAdd.setOnClickListener {
lifecycleScope.launch {
val user = User("18","张三","男","zhangsan@qq.com")
withContext(Dispatchers.IO) {
roomUtils.insertUser(user)
LogUtils.d(TAG, "----添加用户数据----$user")
}
Snackbar.make(binding.tvAdd,user.toString(),Snackbar.LENGTH_LONG).show()
}
}
binding.tvUpdate.setOnClickListener {
lifecycleScope.launch {
val user = User("12","Tom","女","ousi@example.com")
withContext(Dispatchers.IO){
roomUtils.updateUser(user)
LogUtils.d(TAG, "----更新用户数据----$user")
}
Snackbar.make(binding.tvUpdate,user.toString(),Snackbar.LENGTH_LONG).show()
}
}
binding.tvQuery.setOnClickListener {
lifecycleScope.launch {
val user = withContext(Dispatchers.IO){
RoomUtils.getInstance().getAllUsers()
}
LogUtils.d(TAG, "----查询用户数据----$user")
Snackbar.make(binding.tvQuery,user.toString(),Snackbar.LENGTH_LONG).show()
}
}
binding.tvDelete.setOnClickListener {
lifecycleScope.launch {
val user = User("18","张三","男","zhangsan@qq.com")
withContext(Dispatchers.IO){
RoomUtils.getInstance().deleteUser(user)
}
LogUtils.d(TAG, "----删除一条用户数据----$user")
Snackbar.make(binding.tvDelete,user.toString(),Snackbar.LENGTH_LONG).show()
}
}
binding.tvDeleteUsers.setOnClickListener {
lifecycleScope.launch {
val user = roomUtils.getAllUsers()
val user1 : List<User>
// 删除所有用户
withContext(Dispatchers.IO) {
roomUtils.deleteUsers(user)
user1 = roomUtils.getAllUsers()
}
LogUtils.d(TAG, "===删除多个用户数据===$user1")
Snackbar.make(binding.tvDelete,user1.toString(),Snackbar.LENGTH_LONG).show()
}
}
binding.tvDeleteAll.setOnClickListener {
lifecycleScope.launch {
val user : List<User>
// 删除所有用户
withContext(Dispatchers.IO) {
roomUtils.deleteAllUser()
user = roomUtils.getAllUsers()
}
LogUtils.d(TAG, "===删除所有用户数据===$user")
Snackbar.make(binding.tvDelete,user.toString(),Snackbar.LENGTH_LONG).show()
}
}
}
}
12.日志打印:
2025-03-30 22:53:13.383 3712-3760 Mainivity com.example.ksproomdemo D ----添加用户数据----User(userId=18, name=张三, sex=男, email=zhangsan@qq.com)
2025-03-30 22:53:26.322 3712-3760 Mainivity com.example.ksproomdemo D ----更新用户数据----User(userId=12, name=Tom, sex=女, email=ousi@example.com)
2025-03-30 22:53:37.349 3712-3712 Mainivity com.example.ksproomdemo D ----查询用户数据----[User(userId=18, name=张三, sex=男, email=zhangsan@qq.com), User(userId=18, name=张三, sex=男, email=zhangsan@qq.com), User(userId=18, name=张三, sex=男, email=zhangsan@qq.com)]
2025-03-30 22:53:49.061 3712-3712 Mainivity com.example.ksproomdemo D ----删除一条用户数据----User(userId=18, name=张三, sex=男, email=zhangsan@qq.com)
2025-03-30 22:53:58.775 3712-3712 Mainivity com.example.ksproomdemo D ===删除多个用户数据===[]
Mainivity com.example.ksproomdemo D ===删除所有用户数据===[]
13.实现效果如下:
14.总结:
- ksp要添加相应插件和依赖,配置方式有所改变.
- ksp中room的路径要配置正确.
- ksp中jvm版本要对应.
- 升级到最新的gradle后compileSdk版本要使用最新的.