Android在ksp中简单使用Room

Android在ksp中简单使用Room

最近下载了最新版Studio,在github上看到很多比较好的开源项目,于是下载下来研究了一下,好多依赖和配置都需要升级,之前使用过room封装数据库工具类,最近在整理ksp相关,周末心血来潮把room也升级了,简单记录一下升级过程,直接上代码。

1. 简介

1.1 什么是 KSP?

KSP 是一个用于处理 Kotlin 代码的编译时工具,它允许开发者在编译期间分析和生成代码。与 KAPT 相比,KSP 直接处理 Kotlin 代码,避免了将 Kotlin 代码转换为 Java 代码的开销,因此性能更高。

2 .KSP 的优势:

  • 更快的编译速度:KSP 直接处理 Kotlin 代码,避免了 KAPT 的额外转换步骤。
  • 更好的 Kotlin 支持 :KSP 完全支持 Kotlin 的特性,如扩展函数、数据类等。
  • 简洁的 API:KSP 提供了更易用的 API,简化了注解处理器的开发。

3. KSP 的核心概念

3.1 符号(Symbol)

KSP 的核心是符号(Symbol),它代表了 Kotlin 代码中的各种元素,如类、函数、属性等。KSP 提供了丰富的 API 来访问这些符号。

3.2 处理器(Processor)

KSP 处理器是一个实现了 SymbolProcessor 接口的类,用于处理 Kotlin 代码中的符号。每个处理器可以注册对特定注解的兴趣,并在编译时处理这些注解。

3.3 代码生成

KSP 处理器可以生成新的 Kotlin 或 Java 代码。生成的代码会被编译器编译,并与原始代码一起打包。

4.添加ksp依赖配置:

ini 复制代码
#KSP
ksp = "2.1.10-1.0.29"
​
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }

5.项目的依赖配置:

scss 复制代码
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)
}

6.app的build.gradle配置:

scss 复制代码
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.ksp)
}

7.添加room配置:

ini 复制代码
#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",
]

8.在build.gradle添加room:

8.1 以往的room配置:

bash 复制代码
 implementation "androidx.room:room-runtime:$room_version"
 kapt "androidx.room:room-compiler:$room_version"
 implementation "androidx.room:room-ktx:$room_version"
 implementation "androidx.room:room-rxjava2:$room_version"

8.2 现在的room配置:

scss 复制代码
api(libs.bundles.room)
ksp(libs.androidx.room.compiler)

9.room数据库生成文件路径:

9.1 以前的路径配置:

ini 复制代码
    //指定room.schemaLocation生成的文件路径
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
            }
        }

9.2 ksp中的路径配置:

ini 复制代码
​
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")
        }
    }

9.3 room对应的jvm版本:

ini 复制代码
compileOptions {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
    jvmTarget = "17"
}
ksp {
    arg("jvmTarget", "17")
}

9.4 生成的数据库文件如下:

bash 复制代码
{
  "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')"
    ]
  }
}

10.布局文件:

ini 复制代码
<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>

11.数据库工具类RoomUtils:

kotlin 复制代码
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()
    }
}

12.UserRepository:

kotlin 复制代码
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()
        }
    }
}

13.Dao和实体类:

less 复制代码
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()
}
less 复制代码
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
}

14.测试代码:

kotlin 复制代码
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","张三","男","[email protected]")
                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","女","[email protected]")
                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","张三","男","[email protected]")
                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()
            }
        }
    }
}

15.日志打印:

less 复制代码
2025-03-30 22:53:13.383  3712-3760  Mainivity               com.example.ksproomdemo              D  ----添加用户数据----User(userId=18, name=张三, sex=男, [email protected])
​
2025-03-30 22:53:26.322  3712-3760  Mainivity               com.example.ksproomdemo              D  ----更新用户数据----User(userId=12, name=Tom, sex=女, [email protected])
​
2025-03-30 22:53:37.349  3712-3712  Mainivity               com.example.ksproomdemo              D  ----查询用户数据----[User(userId=18, name=张三, sex=男, [email protected]), User(userId=18, name=张三, sex=男, [email protected]), User(userId=18, name=张三, sex=男, [email protected])]
​
2025-03-30 22:53:49.061  3712-3712  Mainivity               com.example.ksproomdemo              D  ----删除一条用户数据----User(userId=18, name=张三, sex=男, [email protected])
​
2025-03-30 22:53:58.775  3712-3712  Mainivity               com.example.ksproomdemo              D  ===删除多个用户数据===[]
  Mainivity               com.example.ksproomdemo              D  ===删除所有用户数据===[]

16.实现效果如下:

17.总结:

  • KSP 是一个强大的 Kotlin 编译时工具,适用于代码生成、代码分析等场景。与 KAPT 相比,KSP 具有更高的性能和更好的 Kotlin 支持。
  • ksp要添加相应插件和依赖,配置方式有所改变.
  • ksp中room的路径要配置正确,ksp中jvm版本要对应.
  • ksp的编译速度和依赖方式简直不要太安逸.

18.项目源码:

gitee.com/jackning_ad...

相关推荐
高林雨露1 小时前
Kotlin 基础语法解析
android·开发语言·kotlin
louisgeek1 小时前
Android 系统架构
android
鸿蒙布道师1 小时前
鸿蒙NEXT开发随机工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
tangweiguo030519871 小时前
(Kotlin)Android 高效底部导航方案:基于预定义 Menu 和 ViewPager2 的 Fragment 动态绑定实现
android·开发语言·kotlin
顾林海2 小时前
Jetpack Pager 使用与原理解析
android·android jetpack
pengyu2 小时前
系统化掌握Dart网络编程之Dio(一):筑基篇
android·flutter·dart
QING6182 小时前
Kotlin 操作符与集合/数组方法详解——新手指南
android·kotlin·app
张风捷特烈2 小时前
Flutter 伪 3D 绘制#1 | 三维空间
android·flutter
stringwu2 小时前
白话kotlin协程
android
QING6182 小时前
Kotlin 中 == 和 === 的区别
android·kotlin·app