이번 글에선 안드로이드 로컬 DB인 Room을 사용하는 방법에 대해서 알아보도록 하자.
ROOM
안드로이드를 사용하다보면 데이터를 로컬에 저장해서 사용해야 할 때가 있다.
이런 때를 위해서 안드로이드에서는 로컬에 데이터를 저장할 수 있는 DB를 지원하고 있다.
예전에는 SQLite를 사용했었는데, 최근에는 안드로이드 문서에서도 Room사용을 적극 권장하고 있다.
Room을 사용하기 위해선 기본적으로 세 가지가 필요한데,
- Entity
- Dao
- Database
위 세 가지를 각각 정의해주어야 Room사용이 가능하다.
일단 Room을 사용하기 위해서 dependencies를 추가해주어야 하는데,
안드로이드 스튜디오 Gradle에서 아래 코드를 넣어주자
def room_version = "2.5.0"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
다음으로 위에서 말한 세 가지 요소들을 각각 만들어보도록 하자.
Entity
먼저 Entity는 우리가 mysql등에서 익히 알고있는 Table과 동일하다고 생각하면 된다.
Room에서는 이러한 Entity에 Column을 각각 지정해주어서 data class로 정의해주어야 한다.
@Entity("user")
data class UserEntity (
val name: String,
val age: String,
val sex: String,
val address: String,
val phoneNumber: String
) {
@PrimaryKey(true)
var id: Int = 0
}
이 글에선 유저의 정보를 기록하는 UserEntity를 만들어보도록 하자.
여기서 @Entity에 들어가는 argument는 Table의 이름이라고 생각하면 된다.
또한 Entity정의시 PrimaryKey를 지정해주어야 하는데, 여기선 id를 PrimaryKey로 지정해주도록 하자.
@PrimaryKey에 argument를 true로 지정해줌으로써 autoIncrement가 활성화 된다.
DAO
다음으로 Dao는 해당 테이블에 사용할 수 있는 명령어(Query)들을 함수로 가지는 interface이다.
우리가 주로 사용할 Query들을 이곳에 미리 정의해서 편리하게 사용할 수 있다.
@Dao
interface UserDao {
@Insert
fun insertUser(user: UserEntity)
@Query("SELECT * FROM user")
fun getAllUser(): List<UserEntity>
@Query("SELECT * FROM user WHERE name = :name")
fun getUserByName(name: String): List<UserEntity>
@Update
fun updateUser(user: UserEntity)
@Delete
fun deleteUser(user: UserEntity)
@Query("DELETE FROM user")
fun deleteAllUser()
}
Insert, Select, Update, Delete명령들을 함수로 정의해놓았다.
또한 Query는 argment로 Query명령어를 String으로 주어야 하는데, 이때 parameter를 사용하고 싶을 경우 앞에 콜론(:)을 붙인다.
Database
마지막으로 Database를 정의해주도록 하자.
@Database(entities = [UserEntity::class], version = 1)
abstract class LocalDatabase: RoomDatabase() {
abstract fun getUserDao(): UserDao
}
@Database의 argument로는 이 DB에서 사용할 Entity(Table)의 목록과 version을 넣어주면 된다.
또한 abstract fun으로 해당 Entity들의 Dao를 반환해주는 함수들을 추가해주도록 한다.
이렇게 세 가지 요소들을 모두 정의해주었으면 사전 준비는 모두 끝났다고 볼 수 있다.
여기서 Room사용시 편리성을 위해서 DB와 Dao를 Hilt를 사용해서 받아오도록 해보자.
Inject
DB와 Dao를 Inject를 사용해 쉽게 사용하기 위해서 RoomModule을 정의해주도록 하자.
@Module
@InstallIn(SingletonComponent::class)
class RoomModule {
@Provides
@Singleton
fun provideLocalDatabase(@ApplicationContext context: Context): LocalDatabase =
Room.databaseBuilder(context, LocalDatabase::class.java, "local_database").build()
@Provides
@Singleton
fun provideUserDao(userDatabase: LocalDatabase): UserDao = userDatabase.getUserDao()
}
여기까지 했으면 실제로 사용해보도록 하자.
MainActivity
이 글에선 간단하게 EditText들로부터 Text를 받아와서 DB에 Insert해준 후,
이 데이터들을 RecyclerView에서 보여주는 작업을 수행하도록 해보겠다.
RecyclerView의 Adapter와 ViewHolder에 대한 설명은 생략하도록 하겠다.
궁금하신 분들은 아래 링크에서 Adapter와 ViewHolder를 만드는 법에 대해 확인해보길 바란다.
https://angangmoddi.tistory.com/320
Adpater
class UserAdapter(
private var userList: ArrayList<UserEntity>
): Adapter<UserViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val binding = ItemUserBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return UserViewHolder(binding)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val item = userList[position]
holder.binding.run {
name.text = item.name
age.text = item.age
sex.text = item.sex
address.text = item.address
phoneNumber.text = item.phoneNumber
}
}
override fun getItemCount(): Int = userList.count()
}
ViewHolder
class UserViewHolder(val binding: ItemUserBinding): ViewHolder(binding.root)
item_user.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
tools:background="@color/cardview_dark_background">
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_marginVertical="20dp"
android:layout_weight="1"
android:textColor="@color/white"
android:textSize="15dp"
tools:text="name" />
<TextView
android:id="@+id/age"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_weight="1"
android:textColor="@color/white"
android:textSize="15dp"
tools:text="age" />
<TextView
android:id="@+id/sex"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_weight="1"
android:textColor="@color/white"
android:textSize="15dp"
tools:text="sex" />
<TextView
android:id="@+id/address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_weight="1"
android:textColor="@color/white"
android:textSize="15dp"
tools:text="add" />
<TextView
android:id="@+id/phone_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_weight="1"
android:textColor="@color/white"
android:textSize="15dp"
tools:text="phone" />
</LinearLayout>
MainActivity.kt
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val userList = ArrayList<UserEntity>()
private lateinit var adapter: UserAdapter
@Inject lateinit var userDao: UserDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
initList()
binding.addButton.setOnClickListener {
insertUser()
}
}
private fun initList() {
CoroutineScope(Dispatchers.Main).launch {
val list = withContext(Dispatchers.IO) {
userDao.getAllUser()
}
userList.addAll(list)
adapter = UserAdapter(userList)
binding.recyclerView.adapter = adapter
}
}
private fun insertUser() {
CoroutineScope(Dispatchers.Main).launch {
if (binding.name.text.isNullOrEmpty()) return@launch
val user = UserEntity(
binding.name.text.toString(),
binding.age.text.toString(),
binding.sex.text.toString(),
binding.address.text.toString(),
binding.phoneNumber.text.toString()
)
withContext(Dispatchers.IO) { userDao.insertUser(user) }
userList.add(user)
adapter.notifyItemInserted(userList.lastIndex)
}
}
}
main_activity.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:orientation="horizontal">
<EditText
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_weight="1"
android:hint="name"
android:textSize="16dp" />
<EditText
android:id="@+id/age"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_weight="1"
android:hint="age"
android:inputType="number"
android:textSize="16dp" />
<EditText
android:id="@+id/sex"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_weight="1"
android:hint="sex"
android:textSize="16dp" />
<EditText
android:id="@+id/address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_weight="1"
android:hint="address"
android:textSize="16dp" />
<EditText
android:id="@+id/phone_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_weight="1"
android:hint="phone_number"
android:textSize="16dp" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@color/cardview_dark_background"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="@layout/item_user" />
<Button
android:id="@+id/add_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp"
android:text="add" />
</LinearLayout>
이 예제에선 Dao에 정의한 Insert, Select, Update, Delete중 Insert와 Select만 사용했다.
나머지 Update와 Delete는 직접 해보시는걸 추천한다.
DB확인
또한 안드로이드 스튜디오에서 DB를 확인해 볼 수도 있다.
View -> Tool Windows -> App Inspection-> Database Inspecor
'안드로이드 > 개발관련(Kotlin)' 카테고리의 다른 글
[Android] Number Picker (0) | 2024.01.03 |
---|---|
안드로이드 BottomNavigationView (0) | 2023.04.04 |
안드로이드 리사이클러뷰(RecyclerView) 사용하기 (0) | 2023.04.01 |
안드로이드 RecyclerView 아이템 줌하기(SanpHelper, ScrollListener) (0) | 2023.03.31 |
안드로이드 Circle Progress (로딩 애니메이션) 만들기 (0) | 2023.03.30 |