티스토리 뷰
Room
- Room은 구글에서 제공하는 공식 ORM(Object-realational mapping)이다
- Room 라이브러리를 사용하면 실행 기기에 앱 데이터 캐시를 만들고 네트워크 연결 여부와 관계없이 보여지게 할 수 있다!
- Room은 그림과 같은 3가지 요소들로 구성된다
- DataBase : 데이터베이스 접근 지점을 제공하며 DAO를 관리한다
- Dao : 데이터베이스에 접근할 수 있는 함수들이 정의되어 있으며 이 함수들은 쿼리문을 이용해 구현해줘야 한다
- Entity : 데이터베이스 내의 DB 테이블을 나타낸다
Room 적용하기
✔️build.gradle(app)
dependencies {
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
// optional - Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"
// optional - RxJava support for Room
implementation "androidx.room:room-rxjava2:$room_version"
// optional - Guava support for Room, including Optional and ListenableFuture
implementation "androidx.room:room-guava:$room_version"
// Test helpers
testImplementation "androidx.room:room-testing:$room_version"
}
- 언제나 제일 먼저 app레벨의 gradle에 종속항목을 추가해준다
✔️RecentPlaceEntity
@Entity(tableName = "recentPlace")
data class RecentPlaceEntity (
@PrimaryKey(autoGenerate = true) var id : Int = 0,
@ColumnInfo(name = "placeName") val placeName: String
)
- 나는 최근 검색 기록 저장 기능이 필요한데, 검색어만 보여주면 되므로 거기에 맞게 테이블을 만들어줬다
- Entity 클래스를 정의할 때 적절한 annotation을 사용해야 한다
- @Entity(tableName = "recentPlace") : 테이블명을 recentPlace로 정의한다
- @PrimaryKey : 해당 annotation이 붙은 entity를 primaryKey로 지정한다
- @ColumnInfo(name = "placeName") : 해당 칼럼명을 placeName으로 정의한다
- @Nonnull : entity의 값이 절대 null이 될 수 없음을 뜻한다
- 모든 테이블에는 반드시 PrimaryKey가 필요한데, 나는 (autoGenerate = true) 속성을 이용해서 따로 정의하지 않아도 자동으로 증가하며 저장되는 entity를 정의했다
✔️BaseDao
interface BaseDao<T> {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(obj : T)
@Delete
fun delete(obj : T)
@Update(onConflict = OnConflictStrategy.ABORT)
fun update(obj : T)
}
- 가장 기본적인 삽입, 삭제, 수정 세 개의 메소드를 포함하는 BaseDao를 인터페이스로 만들어줬다
- Dao를 정의할 때 이 인터페이스를 상속받으면 override 없이 해당 함수에 바로 접근할 수 있다!
✔️RecentPlaceDao
@Dao
interface RecentPlaceDao : BaseDao<RecentPlaceEntity>{
@Query("SELECT * FROM recentPlace ORDER BY id DESC")
fun loadRecentPlace(): LiveData<List<RecentPlaceEntity>>
}
- RecentPlaceDao에는 최근 검색 기록을 가져올 때 필요한 메소드를 정의해주었다
- Observe해서 검색 진행중에도 데이터 변경 시 바로 업데이트해줄 수 있도록 List형태의 LiveData를 반환하는 함수로 정의해줬다
✔️RecentPlaceDataBase
@Database(entities = [RecentPlaceEntity::class], version = 1)
abstract class RecentPlaceDataBase : RoomDatabase() {
abstract fun recentPlaceDao() : RecentPlaceDao
companion object {
private var instance: RecentPlaceDB? = null
@Synchronized
fun getInstance(context: Context): RecentPlaceDB? {
if (instance == null) {
instance = Room.databaseBuilder(context.applicationContext,
RecentPlaceDB::class.java, "RecentPlaceDB")
.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.build()
}
return instance
}
}
}
- DataBase 클래스는 반드시 RoomDataBase를 상속받는 추상클래스로 정의해야 한다
- annotation에는 데이터베이스와 연결할 entity를 배열 형태로 지정해준다
- RoomDataBase 인스턴스는 리소스를 아주 많이 소비하므로 Room.databaseBuilder()를 이용해 싱글톤으로 생성해줘야한다
- 싱글톤 중복생성을 막기 위해 인스턴스 생성 메소드에 @Synchronized 주석을 사용했다
✔️PlaceSearchRepository
class PlaceSearchRepository{
private val database = RecentPlaceDB.getInstance(EarlyBuddyApplication.getGlobalApplicationContext())!!
private val recentPlaceDao = database.recentPlaceDao()
fun insert(recentPlace: RecentPlaceEntity) {
InsertAsyncTask(recentPlaceDao).execute(recentPlace)
}
fun loadRecentPlace(): LiveData<List<RecentPlaceEntity>>{
return recentPlaceDao.loadRecentPlace()
}
private class InsertAsyncTask<TE, TDO : BaseDao<TE>>(private val dao: TDO) : AsyncTask<TE, Void, Void>() {
override fun doInBackground(vararg entity: TE): Void? {
dao.insert(entity[0])
return null
}
}
}
- 함수 호출 시 백그라운드에서 작동하도록 AsyncTask를 이용해 함수를 구현해준다
✔️PlaceSearchViewModel
class PlaceSearchViewModel : BaseViewModel(EarlyBuddyApplication.getGlobalApplicationContext()) {
private val repository : PlaceSearchRepository = PlaceSearchRepository()
val places: LiveData<List<RecentPlaceEntity>>
fun insert(recentPlace : RecentPlaceEntity) {
repository.insert(recentPlace)
}
init {
places = repository.loadRecentPlace()
}
}
- activity를 처음 띄웠을 때 최근 검색 리스트를 띄울 수 있도록 init에서 select 쿼리문을 실행하는 함수를 불러와 LiveData를 업데이트 해준다
- insert 함수에서는 repository에 구현된 insert 함수에 접근한다
✔️PlaceSearchActivity
viewModel.insert(RecentPlaceEntity(placeName = act_start_place_search_et_search.text.toString()))
- 검색 결과를 최근 검색 기록에 저장하는 코드이다
- Room 데이터를 사용할 activity에서는 뷰모델 객체를 이용해 PlaceSearchViewModel에 정의되어 있는 insert 함수에 접근한다
- id는 autoGenerate로 정의했으므로 값을 따로 넣어줄 필요 없다
viewModel.places.observe(this, Observer {
(viewDataBinding.actStartPlaceSearchRv.adapter as BaseRecyclerViewAdapter<RecentPlaceEntity, ItemRecentPlaceBinding>)
.replaceAll(it)
(viewDataBinding.actStartPlaceSearchRv.adapter as BaseRecyclerViewAdapter<RecentPlaceEntity, ItemRecentPlaceBinding>)
.notifyDataSetChanged()
})
- 또한, activity에서 LiveData를 Observe해서 데이터 변경이 일어날 시 바로 리싸이클러뷰에 반영해줄 수 있게 했다!
참고자료🕊
'Android' 카테고리의 다른 글
[Android] 안드로이드 Activity와 Fragment의 생명주기 (0) | 2020.06.23 |
---|---|
[Android] 어플리케이션 기본 항목 - 4대 컴포넌트, App Manifest (0) | 2020.06.23 |
[kotlin] DI 의존성 주입 - koin 사용하기 (0) | 2020.06.14 |
[Android & kotlin] AAC(Android Architecture Components) - ViewModel (0) | 2020.06.04 |
[디자인패턴] MVVM 패턴 (BaseActivity, BaseViewModel 사용하기) (0) | 2020.05.03 |