kapt란 Kotlin Annotation Processing 의 약자로서 코틀린에서 Annotation Processing를 사용하기 위하여 필요하다.
plugins {
id 'kotlin-kapt'
}
플러그인에 추가해준다.
android {
buildFeatures {
viewBinding true
}
}
또한 익스텐션대신 뷰바인딩을 사용할 것이기 때문에 추가해준다.
레이아웃 생성
간단하게 테스트할수 있는 레이아웃을 만들어준다.
이름과 나이를 등록하고, 밑에 텍스트뷰에는 등록된 데이터들이 나열되도록 할 것이다.
Entity 생성
@Entity
data class User (
var name : String,
var age : String
) {
@PrimaryKey(autoGenerate = true)
var id = 0
}
@Entity 어노테이션으로
Entity를 생성해준다.
@PrimaryKey 어노테이션으로 프라이머리키를 지정해줄수 있으며, autoGenerate 값을 true로 설정하면 id 값을 자동으로 생성해준다.
이 데이터클래스가 데이터베이스의 테이블 역할을 한다.
또한 @Embedded 어노테이션으로 다른 object를 인수로 받을 수 있는데,
@Entity
data class User (
var name : String,
var age : String
@Embedded
var userdata : Userdata
) {
@PrimaryKey(autoGenerate = true)
var id = 0
}
data class Userdata(
var phone : String,
var address : String
)
위와 같이 Userdata 클래스를 받아와서 사용할 수도 있다.
DAO 생성
@Dao
interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(user : User)
@Update
suspend fun update(user : User)
@Delete
suspend fun delete(user : User)
@Query("SELECT * FROM User")
suspend fun getAll() : List<User>
@Query("DELETE FROM User ")
suspend fun deleteAll()
}
@Dao 어노테이션으로
Dao를 생성해준다.
DB 메소드들은 기본적으로 코루틴에서 실행되기 때문에 suspend로 선언해준다.
@Insert, @Update, @Delete
어노테이션은 각각 삽입, 수정, 삭제의 기능을 하며
삽입에서 onConflict = OnConflictStrategy.REPLACE 를 사용하면 데이터베이스 내에 중복된 튜플( 동일한 ID) 을 삽입할 경우 덮어씌우게 해 준다.
이외의 메서드는 @Query로 쿼리를 직접 작성하면된다.
여기서는 예시로 테이블 내 데이터를 전부 조회하는 getAll
테이블 내 데이터를 전부 삭제하는 deleteAll를 쿼리로 작성하였다.
Room Database 생성
// entities = 사용할 엔티티 선언, version = 엔티티 구조 변경 시 구분해주는 역할
@Database(entities = [User::class], version = 1)
abstract class UserDatabase : RoomDatabase() {
abstract fun userDao() : UserDao
companion object{
private var instance : UserDatabase? = null
@Synchronized
fun getInstance(context : Context) : UserDatabase? {
if(instance == null){
synchronized(UserDatabase::class){
instance = Room.databaseBuilder(
context.applicationContext,
UserDatabase::class.java,
"user.db"
).build()
}
}
return instance
}
}
}
데이터베이스를 자주 생성하는것은 비효율적이므로 싱글톤 패턴으로 구현하는것이 권장되어 있기 때문에 싱글톤 패턴으로 구현해준다.
MainActivity에서 사용하기
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var db : UserDatabase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
db = UserDatabase.getInstance(applicationContext)!!
fetchUserList()
}
fun fetchUserList(){
var userListText = "사용자 목록"
CoroutineScope(Dispatchers.Main).launch {
val load = async(Dispatchers.IO) {
val userList = db.userDao().getAll()
for(i in userList){
userListText += "\n${i.id} ${i.name}, ${i.age}"
}
}
load.await()
binding.textView.text = userListText
}
}
fun addUser(view : View){
val user = User(binding.nameEditView.text.toString(),binding.ageEditView.text.toString())
CoroutineScope(Dispatchers.IO).launch {
db.userDao().insert(user)
}
fetchUserList()
}
fun deleteAllUser(view : View){
CoroutineScope(Dispatchers.Main).launch {
val delete = async(Dispatchers.IO) {
db.userDao().deleteAll()
}
delete.await()
fetchUserList()
}
}
}
전체코드에서 하나씩 살펴보면
private lateinit var binding: ActivityMainBinding
private lateinit var db : UserDatabase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
db = UserDatabase.getInstance(applicationContext)!!
fetchUserList()
}
oncreate에서 뷰바인딩을 해주고 db를 초기화해준다.
또한 생성시 데이터를 조회하여 나열하는 메소드인fetchUserList를 호출하여 데이터를 표시해준다.
fun fetchUserList(){
var userListText = "사용자 목록"
CoroutineScope(Dispatchers.Main).launch {
val load = async(Dispatchers.IO) {
val userList = db.userDao().getAll()
for(i in userList){
userListText += "\n${i.id} ${i.name}, ${i.age}"
}
}
load.await()
binding.textView.text = userListText
}
}
코루틴의 메인쓰레드에서
데이터 읽기,쓰기는 Dispatchers.IO 에서 해야하고, UI작업은 메인쓰레드에서 해야하므로 따로 생성해주고
그 쓰레드가 종료되면 텍스트뷰에 텍스트를 할당해준다.
fun addUser(view : View){
val user = User(binding.nameEditView.text.toString(),binding.ageEditView.text.toString())
CoroutineScope(Dispatchers.IO).launch {
db.userDao().insert(user)
}
fetchUserList()
}
이름과 나이를 적은 에디트뷰에서 텍스트를 가져와서 데이터베이스에 넣고 데이터를 다시 표시해준다.
fun deleteAllUser(view : View){
CoroutineScope(Dispatchers.Main).launch {
val delete = async(Dispatchers.IO) {
db.userDao().deleteAll()
}
delete.await()
fetchUserList()
}
}
테이블에 존재하는 데이터를 삭제하고 다시 표시해준다.
등록된 데이터가 없을 때는 이렇게 표시되며
데이터를 등록하면 그 데이터가 출력되고, 어플을 종료하고 다시 실행하여도 저장되어 출력된다.