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","张三","男","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()
            }
        }
    }
}

15.日志打印:

less 复制代码
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  ===删除所有用户数据===[]

16.实现效果如下:

17.总结:

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

18.项目源码:

gitee.com/jackning_ad...

相关推荐
幻雨様1 小时前
UE5多人MOBA+GAS 45、制作冲刺技能
android·ue5
Jerry说前后端3 小时前
Android 数据可视化开发:从技术选型到性能优化
android·信息可视化·性能优化
Meteors.4 小时前
Android约束布局(ConstraintLayout)常用属性
android
alexhilton4 小时前
玩转Shader之学会如何变形画布
android·kotlin·android jetpack
whysqwhw9 小时前
安卓图片性能优化技巧
android
风往哪边走9 小时前
自定义底部筛选弹框
android
Yyyy48210 小时前
MyCAT基础概念
android
Android轮子哥10 小时前
尝试解决 Android 适配最后一公里
android
雨白11 小时前
OkHttp 源码解析:enqueue 非同步流程与 Dispatcher 调度
android
风往哪边走12 小时前
自定义仿日历组件弹框
android