سلام دوستان عزیز! امروز میخوایم با هم یکی از قدرتمندترین ترکیبها در دنیای اندروید رو بررسی کنیم: Room + KSP + Jetpack Compose. این سه تکنولوژی با هم ترکیب میشن تا توسعه اپلیکیشنهای اندرویدی رو خیلی راحتتر و سریعتر کنن.
توی این مقاله، قصد دارم نحوه پیادهسازی این ترکیب رو با مثالهای کاربردی بهتون آموزش بدم. اما قبلش اگه با دیتابیسهای SQL آشناییت ندارید حتما یک آموزش کوتاه از دیتابیس SQLite ببینید و همچنین اگه هنوز با ViewModel در JetPack Compose آشنا نیستید، حتما این آموزش رو نگاه کنید.

آشنایی با Room
Room یک کتابخانه از خانواده Jetpack اندرویده که به عنوان یک لایه انتزاعی (ORM) روی SQLite عمل میکنه. این کتابخانه به ما اجازه میده تا به راحتی با دیتابیس کار کنیم و از مزایای زیر بهرهمند بشیم:
بررسی کوئریها در زمان کامپایل
کوئریهای SQL شما در زمان کامپایل بررسی میشن و خطاها زودتر شناسایی میشن.
کاهش کدهای تکراری
با استفاده از annotation، کدهای تکراری کاهش پیدا میکنن و توسعه سریعتر میشه.
مسیرهای سادهشده برای مهاجرت
فرآیند migration دیتابیس با Room بسیار سادهتر و قابل مدیریتتره.
یکپارچهسازی با Jetpack
Room به خوبی با سایر اجزای Jetpack مانند LiveData و Flow یکپارچه میشه.
اجزای اصلی Room
Room از سه جزء اصلی تشکیل شده:
1. Entity (موجودیت)
Entity نماینده یک جدول در دیتابیس هست. هر Entity با @Entity
مشخص میشه و شامل فیلدهایی هست که ستونهای جدول رو تشکیل میدن.
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
@ColumnInfo(name = "name")
val name: String,
@ColumnInfo(name = "email")
val email: String
)
2. DAO (Data Access Object)
DAO شامل متدهایی برای دسترسی به دیتابیس هست. این متدها با انوتیشنهایی مثل @Query
، @Insert
، @Update
و @Delete
مشخص میشن.
@Dao
interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUser(user: User)
@Query("SELECT * FROM users")
suspend fun getAllUsers(): List<User>
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getUserById(id: Int): User?
}
3. Database
Database کلاسی هست که به عنوان نقطه دسترسی اصلی به دیتابیس عمل میکنه. این کلاس باید از RoomDatabase
ارثبری کنه و با انوتیشن @Database
مشخص بشه.
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}

KSP چیست؟
KSP یا Kotlin Symbol Processing یک ابزار پردازش انوتیشن برای کد Kotlin هست. KSP سریعتر و کارآمدتر ازKAPT (که قبلا استفاده می شد) عمل میکنه.
ویژگی | KSP | KAPT |
---|---|---|
سرعت | تا ۲ برابر سریعتر | کندتر |
یکپارچهسازی | مستقیم با کامپایلر Kotlin | نیاز به تولید Java stubs |
درک کد | درک بهتر از ساختارهای Kotlin | محدودیت در درک کد Kotlin |
وابستگی به JVM | وابستگی کمتر | وابستگی بیشتر |
نکته: گوگل به طور رسمی KSP را به عنوان جایگزین KAPT معرفی کرده و توصیه میکنه که در پروژههای جدید از KSP استفاده بشه.

قبل از اینکه بخوایم دیتابیس Room رو اضافه کنیم، لازم هست تا KSP رو به پروژه اضافه کرده باشیم.
نحوه استفاده از KSP :
برای استفاده از KSP ، باید مراحل زیر رو طی کنید:
1_ افزودن پلاگین KSP (مشاهده آخرین نسخه و تغییرات)

// در فایل build.gradle.kts سطح پروژه
plugins {
id("com.google.devtools.ksp") version "2.0.21-1.0.27" apply false
}
2_ اعمال پلاگین در module-level :

// در فایل build.gradle.kts ماژول
plugins {
id("com.google.devtools.ksp")
}
پیادهسازی Room:
حالا نوبت به Room می رسه:

1- افزودن پلاگین روم:
plugins { id("androidx.room") version "$room_version" apply false }
2- اعمال پلاگین در module-level :

plugins {
...
id("androidx.room")
}
dependencies {
val room_version = "2.7.2"
implementation("androidx.room:room-runtime:$room_version")
ksp("androidx.room:room-compiler:$room_version")
// If this project only uses Java source, use the Java annotationProcessor
// No additional plugins are necessary
annotationProcessor("androidx.room:room-compiler:$room_version")
// optional - Kotlin Extensions and Coroutines support for Room
implementation("androidx.room:room-ktx:$room_version")
}
android {
...
room {
schemaDirectory("$projectDir/schemas")
}
}
تنظیم schemaDirectory هنگام استفاده از افزونه Room Gradle الزامی است. این کار کامپایلر Room و وظایف مختلف کامپایل و backend های آن (javac، KAPT، KSP) را طوری پیکربندی میکند که فایلهای schema را در پوشههای flavored، به عنوان مثال schemas/flavorOneDebug/com.package.MyDatabase/1.json، خروجی دهد. این فایلها باید در مخزن بررسی شوند تا برای اعتبارسنجی و مهاجرت خودکار استفاده شوند.
۳- تعریف Entity :
ما قصد داریم تا یک سیستم ورود آفلاین کاربر بسازیم که ایمیل و کلمه عبورش رو در دیتابیس بررسی کنیم اگر وجود داره، وارد حساب کاربریش بشه، برای اینکار لازم داریم تا یه جدول از کاربرهامون در دیتابیسمون داشته باشیم:

@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true) val id: Int,
val name: String,
val password: String,
val mail: String,
val phone: String,
)
۴ـ تعریف DAO

@Dao
interface UserDAO {
@Insert
suspend fun insert(user: User)
@Query("SELECT * FROM users WHERE mail = :mail AND password = :password")
suspend fun getUser(mail: String, password: String): User?
@Query("SELECT * FROM users ")
suspend fun getAllUsers(): List<User>
}
۵- تعریف Database

@Database(entities = [User::class], version = 1, exportSchema = true)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDAO(): UserDAO
}
abstract class AppDatabase : RoomDatabase()
:abstract class AppDatabase
: این کلاس روabstract
تعریف کردن چون قراره خودش مستقیم ساخته نشه. روم خودش یه نسخهی پیادهسازی شده از این کلاس رو پشت صحنه میسازه.: RoomDatabase()
: این کلاس ازRoomDatabase
ارثبری میکنه.RoomDatabase
کلاس پایهی روم برای مدیریت دیتابیسه
@Database(entities = [User::class], version = 1)
:@Database
: این یه انوتیشن (Annotation) مخصوص رومه. به Room میگه: “ببین این کلاس، کلاس اصلی دیتابیس ماست!”entities = [User::class]
: اینجا مشخص میکنه که دیتابیس ما قراره چند تا جدول (Entity) داشته باشه. اینجا فقط یه جدول به اسمUser
داریم.version = 1
: اینم شماره نسخهی دیتابیس. هر وقت بخوای ساختار جدولها رو عوض کنی (مثلاً یه ستون جدید اضافه کنی یا یه جدول جدید بسازی)، باید این شماره رو افزایش بدی (مثلاً بکنی2
) تا روم متوجه تغییرات بشه.
استفاده در ViewModel
خب حالا که دیتابیسمون رو تعریف کردیم باید در ویومدلمون ازش استفاده کنیم که نیاز به Hilt برای تزریق وابستگی ها به ویو مدل داریم. از اونجا که این مطلبمون خیلی طولانی شده، این موضوع رو در یک پست جداگونه ادامه می دم.
دیدگاهتان را بنویسید