ꡬκΈμμ μ 곡νλ κ΅μ‘μλ£λ₯Ό μ 리νκΈ° μν ν¬μ€νΈμ λλ€.
μ½κ² μμΈμ€, κ²μ λ° κ΅¬μ±ν μ μλ ꡬ쑰νλ λ°μ΄ν° λͺ¨μ. λ€μκ³Ό κ°μ΄ ꡬμ±λμ΄ μμ΅λλ€.
SQLμ μ¬μ©νμ¬ κ΄κ³ν λ°μ΄ν°λ² μ΄μ€μ μ‘μΈμ€νκ³ μμ ν©λλ€.
λͺ¨λ°μΌ κΈ°κΈ°λ νλμ¨μ΄μ μ»΄ν¨ν° λ₯λ ₯μ΄ μ νμ μ΄κΈ° λλ¬Έμ μλλ‘μ΄λμμλ SQL νμ€μ κΈ°λ°μΌλ‘νλ SQLiteλ₯Ό μ¬μ©ν©λλ€. SQLiteλ SQLμ λλΆλΆμ κΈ°λ₯μ μ§μν©λλ€.
SQLiteμ λͺ¨λ κΈ°λ₯μ νμ©ν©λλ€.
dependencies {
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
// Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"
// Test helpers
testImplementation "androidx.room:room-testing:$room_version"
}
λ°μ΄ν°λ² μ΄μ€μ λ°μ΄ν° ννμ μ±μμ μ§μ μ¬μ©ν μ μλ κ°μ²΄λ‘ λ³ννλ κ°μ²΄ κ΄κ³ν 맀ν λΌμ΄λΈλ¬λ¦¬μ λλ€.
Color μ μ₯μ μν΄ νμν ν΄λμ€λ₯Ό μ μν©λλ€. Room databaseμμ μ¬μ©νλ €λ©΄ annotationμ΄ νμν©λλ€.
data class Color {
val hex: String,
val name: String
}
@Entity
, @DAO
, @Database
@Entity(tableName = "colors")
SQLite λ°μ΄ν°λ² μ΄μ€ ν μ΄λΈμ 맀νλλ ν΄λμ€.
@Entity
: ν΄λΉ ν΄λμ€κ° μν°ν°μμ μ립λλ€.@PrimaryKey
: ν
μ΄λΈμ κΈ°λ³Έν€λ₯Ό μ§μ ν©λλ€.@ColumnInfo
: κΈ°λ³Έμ μΌλ‘ Property μ΄λ¦μ μ¬μ©νμ¬ columnμ΄ λ§λ€μ΄ μ§μ§λ§ ν΄λΉ μ΄λ
Έν
μ΄μ
μ μ¬μ©νμ¬ λ³κ²½ν μ μμ΅λλ€.@Entity(tableName = "colors")
data class Color {
@PrimaryKey(autoGenerate = true) val _id: Int,
@ColumnInfo(name = "hex_color") val hex: String,
val name: String
}
λ°μ΄ν°λ² μ΄μ€μ μ‘μΈμ€ νκΈ° μν ν΄λμ€ μμ
@Dao
interface ColorDao {
@Query("SELECT * FROM colors")
fun getAll(): Array<Color>
@Insert
fun insert(vararg color: Color)
@Update
fun update(color: Color)
@Delete
fun delete(color: Color)
@Database
κ³Ό ν¨κ» μν°ν° 리μ€νΈλ₯Ό ν¬ν¨ν©λλ€.@Database(entities = [Color::class], version = 1)
RoomDatabase
λ₯Ό νμ₯ν©λλ€.abstract class ColorDatabase : RoomDatabase()
abstract fun colorDao(): ColorDao
RoomDatabase μΈμ€ν΄μ€λ 무κ²κ³ λ¨μΌ νλ‘μΈμ€ λ΄μμ μ¬λ¬ μΈμ€ν΄μ€μ μ‘μΈμ€ν νμκ° κ±°μ μκΈ° λλ¬Έμ μ±κΈν€ ν¨ν΄μ λ°λΌμΌν©λλ€.
@Database(entities = [Color::class], version = 1)
abstract class ColorDatabase : RoomDatabase() {
abstract fun colorDao(): ColorDao
companion object {
@Volatile
private var INSTANCE: ColorDatabase? = null
fun getInstance(context: Context): ColorDatabase {
...
}
}
...
Room.databaseBuilder()λ₯Ό μ¬μ©νμ¬ λ°μ΄ν°λ² μ΄μ€λ₯Ό λ§λλλ€.
synchronized : νλμ μ€ν μ€λ λλ§ μ΄ μ½λ λΈλ‘μ μννλ―λ‘ λ°μ΄ν°λ² μ΄μ€κ° ν λ²λ§ μ΄κΈ°νλ©λλ€.
fallbackToDestructiveMigration()
: λ§μ΄κ·Έλ μ΄μ
κ²½λ‘κ° λλ½λμμ λ κΈ°μ‘΄ λ°μ΄ν°κ° μμ€λλ κ²μ νμ©ν κ²½μ°.
fun getInstance(context: Context): ColorDatabase {
return INSTANCE ?: synchronized(this) {
INSTANCE ?: Room.databaseBuilder(
context.applicationContext,
ColorDatabase::class.java, "color_database"
)
.fallbackToDestructiveMigration()
.build()
.also { INSTANCE = it }
}
}
λ°μ΄ν°λ² μ΄μ€μμ DAOλ₯Ό κ°μ Έμ΅λλ€.
val colorDao = ColorDatabase.getInstance(application).colorDao()
μλ‘μ΄ Colorλ₯Ό λ§λ€κ³ DAOλ₯Ό μ¬μ©νμ¬ λ°μ΄ν°λ² μ΄μ€μ μ λ ₯ν©λλ€.
val newColor = Color(hex = "#6200EE", name = "purple")
colorDao.insert(newColor)
μ₯κΈ° μμ μ μννλ κ²½μ° κΈ°λ³Έ μ€λ λμμ μννλ©΄ μλ©λλ€. κΈ°λ³Έ μ€λ λμμ μνν κ²½μ° μ±μ΄ μ¬μ©μμκ² μλ΅νμ§ μμ μ μμ΅λλ€.
μλλ‘μ΄λ λΉλκΈ° νλ‘κ·Έλλ°μ κΆμ₯λλ λ°©λ²μ λλ€.
suspend fun insert(word: Word) {
wordDao.insert(word)
}
suspend : νμ¬ μ½λ£¨ν΄μ μ€νμ μΌμ μ€μ§νκ³ μ§μ λ³μλ₯Ό μ μ₯ν©λλ€.
resume : μ μ₯λ μνλ₯Ό μλμΌλ‘ λ‘λνκ³ μΌμ μ€λ¨λ μ§μ λΆν° μ€νν©λλ€.
μ½λ£¨ν΄μ΄ suspend ν¨μλ₯Ό νΈμΆνλ©΄ ν΄λΉ ν¨μκ° μΌλ° ν¨μ νΈμΆμ²λΌ λ°νλ λκΉμ§ μ°¨λ¨νλ κ²μ΄ μλλΌ κ²°κ³Όκ° μ€λΉλ λκΉμ§ μ€νμ μΌμ μ€λ¨ν λ€μ κ²°κ³Όμ ν¨κ» μ€λ¨λ κ³³μμ λ€μ μμν©λλ€.
κ²°κ³Όλ₯Ό κΈ°λ€λ¦¬λ λμ μ€λ λμ μ°¨λ¨μ ν΄μ νμ¬ λ€λ₯Έ κΈ°λ₯μ΄λ μ½λ£¨ν΄μ μ€νν μ μμ΅λλ€.
suspend μ resume μμ μ ν¨κ» μ¬μ©νμ¬ μ½λ°±μ λ체ν©λλ€.
Roomμ μ½λ£¨ν΄μ μ§μν©λλ€. suspend μμ μλ₯Ό μΆκ° νμ¬, μ½λ£¨ν΄μ΄λ λ€λ₯Έ suspend ν¨μμμ νΈμΆν μ μκ² ν©λλ€.
@Dao
interface ColorDao {
@Query("SELECT * FROM colors")
suspend fun getAll(): Array<Color>
@Insert
suspend fun insert(vararg color: Color)
@Update
suspend fun update(color: Color)
@Delete
suspend fun delete(color: Color)
withContextλ₯Ό μ¬μ©νμ¬ κ°μΈμ§ μ½λλ₯Ό μ€νν λμ€ν¨μ³λ₯Ό μ§μ ν μ μμ΅λλ€.
suspend fun get(url: String) {
// Start on Dispatchers.Main
withContext(Dispatchers.IO) {
// Switches to Dispatchers.IO
// Perform blocking network IO here
}
// Returns to Dispatchers.Main
}
μ½λ£¨ν΄μ CoroutineScopeμμ μ€νλμ΄μΌ ν©λλ€.
Examples:
fun loadUI() {
launch {
fetchDocs()
}
}
class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
// Coroutine that will be canceled
// when the ViewModel is cleared
}
}
...
class ColorViewModel(val dao: ColorDao, application: Application)
: AndroidViewModel(application) {
fun save(color: Color) {
viewModelScope.launch {
colorDao.insert(color)
}
}
...
android {
defaultConfig {
...
testInstrumentationRunner "androidx.test.runner
.AndroidJUnitRunner"
testInstrumentationRunnerArguments clearPackageData: 'true'
}
}
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}
ν
μ€νΈμ νμν DAOμ λ°μ΄ν°λ² μ΄μ€λ₯Ό μ μν©λλ€.
λμ€μ μ¬μ©ν Color μΈμ€ν΄μ€λ₯Ό μμ±ν΄ μ€λλ€.
@RunWith(AndroidJUnit4::class)
class DatabaseTest {
private lateinit val colorDao: ColorDao
private lateinit val db: ColorDatabase
private val red = Color(hex = "#FF0000", name = "red")
private val green = Color(hex = "#00FF00", name = "green")
private val blue = Color(hex = "#0000FF", name = "blue")
...
@Before
: ν
μ€νΈ μ΄μ μ μνν νλμΌλ‘ λ°μ΄ν°λ² μ΄μ€μ DAO μΈμ€ν΄μ€λ₯Ό μμ±ν©λλ€.
(μ€μ μ¬μ©μ λ°μ΄ν°λ₯Ό μ μ§νκΈ° μν΄ λ μ΄μ μ¬μ©νμ§ μμ λ νκΈ°λλ ν
μ€νΈ λͺ©μ μΌλ‘ inMemoryDatabaseBuilder
λ₯Ό μ¬μ©ν©λλ€.)
@After
: ν
μ€νΈ μλ£λ ν μνλ©λλ€.
@Before
fun createDb() {
val context: Context = ApplicationProvider.getApplicationContext()
db = Room.inMemoryDatabaseBuilder(context, ColorDatabase::class.java)
.allowMainThreadQueries()
.build()
colorDao = db.colorDao()
}
@After
@Throws(IOException::class)
fun closeDb() = db.close()
@Test
@Throws(Exception::class)
fun insertAndRetrieve() {
colorDao.insert(red, green, blue)
val colors = colorDao.getAll()
assert(colors.size == 3)
}