Android room 사용법 및 설정 방법 (viewModel & repository)
04 May 2024 -
7 mins read time
Tags:
Android
Kotlin
1. Gradle 설정
plugins {
id 'kotlin-kapt' // 추가하기
}
dependencies {
// Room DB
def room_version = "2.6.1"
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"
}
- App 수준의 gradle 파일 상단에 위의 코드를 추가해주세요.
- App 수준의 dependency 에 room 의존성을 추가해주세요
2. repository & dao & model 만들기
@Entity
data class JobSite(
@PrimaryKey(autoGenerate = true) val uid: Int,
@ColumnInfo(name = "companyName") var companyName: String?,
@ColumnInfo(name = "companyUrl") var companyUrl: String?
) {
constructor(companyName: String?, companyUrl: String?) : this(0, companyName, companyUrl)
}
- room 에 들어간 모델인 data class 를 만들고 @Entity 어노테이션을 추가해주세요
- @PrimaryKey(autoGenerate = true) 는 기본키를 의미하고 자동생성 설정 가능합니다
- @ColumnInfo 는 사용하는 변수명과 Room 에서 쓰는 변수명을 다르게 하기 위함입니다
@Dao
interface JobSiteDao {
@Query("SELECT * FROM JobSite")
fun getAll(): Flow<List<JobSite>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(jobSite: JobSite)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(vararg jobSites: JobSite)
@Delete
fun delete(jobSite: JobSite)
}
- @dao annotation 을 추가해서 인터페이스가 db 접근을 위함이다는 것을 알려줍니다
- @Query, @Insert, @Delete 를 통해 database 에 접근할 수 있습니다.
class JobSiteRepository(private val jobSiteDao: JobSiteDao) {
val allJobSites: Flow<List<JobSite>> = jobSiteDao.getAll()
@WorkerThread
suspend fun insert(jobSite: JobSite) {
jobSiteDao.insert(jobSite)
}
@WorkerThread
suspend fun update(jobSite: JobSite) {
jobSiteDao.update(jobSite)
}
@WorkerThread
suspend fun delete(jobSite: JobSite) {
jobSiteDao.delete(jobSite)
}
}
- viewModel 에서 호출할 Repository 를 만들어 줍니다.
- DB or 네트워크 요청은 메인 쓰레드가 아닌 코루틴에서 동작해야 하기에 suspend 키워드를 사용 합니다.
3. AppDataBase 파일 만들기
@Database(entities = [JobSite::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun jobSiteDao(): JobSiteDao
companion object {
@Volatile
private var INSTANCE: AppDatabase? = null
fun getInstance(
context: Context,
scope: CoroutineScope
): AppDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"word_database"
)
// Wipes and rebuilds instead of migrating if no Migration object.
// Migration is not part of this codelab.
.fallbackToDestructiveMigration()
.addCallback(JobSiteDatabaseCallback(scope))
.build()
INSTANCE = instance
// return instance
instance
}
}
}
private class JobSiteDatabaseCallback(
private val scope: CoroutineScope
) : RoomDatabase.Callback() {
/**
* Override the onCreate method to populate the database.
*/
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
// If you want to keep the data through app restarts,
// comment out the following line.
INSTANCE?.let { database ->
scope.launch(Dispatchers.IO) {
Log.e(javaClass.simpleName, "DB 성공 이후 데이터 초기화")
val jobSites = mutableListOf<JobSite>().apply {
add(JobSite(companyName = "삼성", companyUrl = "https://www.samsungcareers.com/hr/"))
}.toTypedArray()
val dao = database.jobSiteDao()
dao.insertAll(*jobSites)
}
}
}
}
}
- @Database annotation 을 통해 버전과 사용할 모델 데이터 클래스를 entities 에 추가
- Database 에 접근할 INSTANCE 를 singleTon 객체로 만들 database init 성공시 초기화 값을 콜백으로 세팅해줍니다.
4. Application & ViewModel 설정
class JobSiteApplication: Application() {
private val applicationScope = CoroutineScope(SupervisorJob())
private val database by lazy { AppDatabase.getInstance(applicationContext, applicationScope)}
val jobSiteRepository by lazy { JobSiteRepository(database.jobSiteDao()) }
}
// AndroidManifest.xml
<application
android:name=".JobSiteApplication" />
- Application 파일을 하나 만들고 프로젝트 내에서 접근이 가능한 database, repository 를 선언해줍니다.
- Application 파일을 만든 후 AndroidManifest.xml 파일 name 속성에 추가해줘야 합니다.
class ListViewModel(private val repository: JobSiteRepository) : ViewModel() {
val jobSites: LiveData<List<JobSite>> = repository.allJobSites.asLiveData()
fun insert(jobSite: JobSite) {
viewModelScope.launch(Dispatchers.IO) {
repository.insert(jobSite)
}
}
fun update(jobSite: JobSite) {
viewModelScope.launch(Dispatchers.IO) {
repository.update(jobSite)
}
}
fun delete(jobSite: JobSite) {
viewModelScope.launch(Dispatchers.IO) {
repository.delete(jobSite)
}
}
}
class MyViewModelFactory(private val repository: JobSiteRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(ListViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return ListViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
- viewModel 의 생성자에 repository 의존성을 추가해주세요
- 전체 리스트는 코루틴의 flow 를 통해 순차적으로 받아서 화면에 보여줄 수 있게 설정합니다
- ViewModelFactory 같은 경우는 ViewModel 을 생성할 때 Application 클래스에서 선언한 repository 의존성을 주입하여 사용하기 위함입니다
5. UI 에서 room 호출
class ListFragment : Fragment() {
// 뷰모델 선언
private val listViewModel: ListViewModel by viewModels { MyViewModelFactory((requireActivity().application as JobSiteApplication).jobSiteRepository) }
// LiveData 관찰
private fun initObserveLiveData() {
listViewModel.jobSites.observe(viewLifecycleOwner) { jobSites ->
// your logic
}
}
}
- ViewModel 변수를 만들 때 ViewModelFactory 를 통해 repostory 의존성을 전달
- liveData 를 관찰하여 데이터 변동에 따라 로직을 처리하면 됨
결론
- room 이 realm 보다 좋다
- application 에 repository, appDatabase 선언하는 것이 불편하면 Hilt 를 통한 의존성 주입을 적용하면 된다.
- 그러나, 대규모의 프로젝트가 아니라면 hilt 보다는 평범하게 하는게 좋음
Related Posts