티스토리 뷰
Room?
Room은 AAC(Android Architecture Components)로
간단히 말하면 스마트폰 내장 DB에 데이터를 저장하기 위해 사용하는 라이브러리이다.
- ORM(Object Relational Mapping) 라이브러리로 DB 데이터를 자바 또는 코틀린 객체로 mapping 해준다.
- SQLite를 내부적으로 사용하고 있지만, DB를 구조적으로 분리하여 데이터 접근의 편의성을 높여주고 유지보수에 편리하다.
- 다양한 Annotation을 통해 컴파일 시 코드들을 자동으로 만들어주며 LiveData, RxJava와 같은 Observation 형태를 지원한다.
- MVP, MVVM과 같은 아키텍쳐 패턴에 쉽게 활용할 수 있도록 되어있다.
Room 사용 시의 이점
- SQL 쿼리의 컴파일 시간 확인
- 반복적이고 오류가 발생하기 쉬운 상용구 코드를 최소화하는 편리한 주석
- 간소화된 데이터베이스 이전 경로
-> 따라서 SQLite API를 직접 사용하는 대신 Room을 사용하는 것이 좋다.
Room 사용을 위한 Dependency 추가
apply plugin: 'kotlin-kapt'
dependencies {
implementation "android.arch.persistence.room:runtime:1.1.1"
kapt "android.arch.persistence.room:compiler:1.1.1"
kaptTest "android.arch.persistence.room:testing:1.1.1"
}
Room의 기본 구성요소
데이터 항목(data entities)
- 앱 데이터베이스의 테이블을 나타낸다.
- 각 Entity는 고유 식발자인 PrimaryKey(기본키)가 반드시 필요하며, 큰 의미가 없다면 autoGenerate를 이용하여 자동으로 생성이 가능하다.
@Entity
data class MyToDoList(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
@ColumnInfo(name="title") val title: String,
@ColumnInfo(name="content") val content: String
)
데이터 액세스 객체(DAO: data access objects)
- 앱이 데이터베이스의 데이터를 쿼리, 업데이트, 삽입, 삭제하는데 사용할 수 있는 메서드를 제공한다.
@Dao
interface ToDoDao {
// 테이블에 데이터 삽입
@Insert(onConflict = REPLACE) // Insert 할 때 PrimaryKey가 겹치는 것이 있으면 덮어 쓴다는 의미
fun insert(toDo: MyToDoList)
// 테이블의 데이터 수정
@Update
fun update(toDo: MyToDoList)
// 테이블의 데이터 삭제
@Delete
fun delete(toDo: MyToDoList)
// 테이블의 모든 값 가져오기
@Query("SELECT * FROM MyToDoList")
fun getAll(): List<MyToDoList>
// 'title'에 해당하는 투두를 삭제
@Query("DELETE FROM MyToDoList WHERE title = :title")
fun deleteToDoByTitle(title: String)
}
데이터베이스 클래스(database class)
- 데이터베이스를 보유하고 앱의 영구 데이터와의 기본 연결을 위한 기본 액세스 포인터 역할을 한다.
- Entity 모델을 기반으로 하고, DAO의 메소드를 가지고 있는 데이터베이스를 생성한다.
- 이는 RoomDatabase()를 상속한다.
@Database(entities = [MyToDoList::class], version = 4)
abstract class ToDoDatabase: RoomDatabase() {
abstract fun getToDoDao(): ToDoDao
// 데이터베이스 객체를 인스턴스 할 때 싱글톤으로 구현
companion object {
private var instance: ToDoDatabase? = null
// 데이터베이스 객체를 반환
@Synchronized
fun getInstance(context: Context): ToDoDatabase? {
if(instance == null){
synchronized(ToDoDatabase::class){
instance = Room.databaseBuilder(
context.applicationContext,
ToDoDatabase::class.java,
"toDo-database"
).fallbackToDestructiveMigration().build()
}
}
return instance
}
// 데이터베이스 객체를 삭제
fun destroyInstance() {
instance = null
}
}
}
다양한 Room 구성요소 간 관계
데이터베이스 클래스는 데이터베이스와 연결된 DAO 인스턴스를 앱에 제공한다.
그러면 앱은 DAO를 사용하야 데이터베이스의 데이터를 연결된 데이터 항목 객체의 인스턴스로 검색할 수 있게 된다.
앱은 정의된 데이터 항목을 사용하여 상응하는 테이블의 행을 업데이트하거나 삽입할 새 행을 만들 수도 있다.
아래 그림은 다양한 Room 구성요소 간 관계를 보여준다.
Activity에서 Room에 접근하기
메인 쓰레드에서 Room DB에 접근하려고 하면 다음 에러가 발생한다.
Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
메인 UI 화면이 오랫동안 멈춰있을 수도 있기 때문에, 메인 쓰레드에서는 데이터베이스에 접근할 수 없습니다.
Room과 관련된 액션은 백그라운드에서 작업해야 한다. 이때, 쓰레드, 코루틴을 사용할 수 있다!
아래 코드는 쓰레드를 이용하였다.
Room에서 데이터를 불러와 리사이클러뷰를 이용해 UI에 나타내주는 코드이다.
추가로 안드로이드 OS는 UI 자원 사용은 UI Thread에서만 가능하므로 Sub Thread에서 UI 자원을 다루기 위해서는 runOnUiThread를 사용해야 한다.
class ToDoActivity : AppCompatActivity(), ToDoContract.View {
// 사용자의 Input이 주어지면 뷰를 통해 Presenter로 흐름이 이어지므로
// 뷰가 Presenter를 알고 있어야 한다.
override lateinit var presenter: ToDoContract.Presenter
private lateinit var myToDoSet: List<MyToDoList>
private lateinit var recyclerView: RecyclerView
private lateinit var toDoAdapter: ToDoAdapter
private var db: ToDoDatabase? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityToDoBinding.inflate(layoutInflater)
setContentView(binding.root)
presenter = ToDoPresenter(this)
recyclerView = binding.todoRecyclerView
db = ToDoDatabase.getInstance(applicationContext) ?: throw IllegalAccessException()
// 룸에 있는 데이터 불러오기
getAllTodo()
binding.mainAddButton.setOnClickListener {
startAddToDoActivity()
}
}
override fun setRecyclerView() {
// 안드로이드 OS는 UI 자원 사용은 UI Thread에서만 가능하므로
// Sub Thread에서 UI 자원을 다루기 위해 runOnUiThread를 사용
runOnUiThread {
toDoAdapter = ToDoAdapter(myToDoSet)
toDoAdapter.notifyDataSetChanged()
recyclerView.apply {
adapter = toDoAdapter
setHasFixedSize(true)
}
}
}
override fun getAllTodo() {
// 메인 쓰레드에서 Room DB 접근 시 에러가 발생하므로 백그라운드에서 작업해야 한다.
Thread{
myToDoSet = db!!.getToDoDao().getAll()
// 리사이클러뷰 설정
setRecyclerView()
}.start()
}
override fun onResume() {
super.onResume()
}
override fun onRestart() {
super.onRestart()
getAllTodo()
}
override fun onDestroy() {
super.onDestroy()
ToDoDatabase.destroyInstance()
db = null
}
private fun startAddToDoActivity(){
val intent = Intent(this, AddToDoActivity::class.java)
startActivity(intent)
}
}
참고
https://developer.android.com/training/data-storage/room#kts
https://blog.yena.io/studynote/2018/09/08/Android-Kotlin-Room.html
'안드로이드[Kotlin]' 카테고리의 다른 글
[안드로이드/Kotlin] ViewModel이란? (1) | 2022.03.13 |
---|---|
[안드로이드/Kotlin] 목록 화면을 만들기 위한 RecyclerView (0) | 2022.02.07 |
[아키텍처 패턴] MVP 패턴이란? (0) | 2022.02.02 |
[안드로이드/Kotlin] 뷰 바인딩이란? (1) | 2022.01.22 |
[아키텍처 패턴] MVC 패턴이란? (0) | 2022.01.12 |