서버-클라이언트 구조에서 대규모 데이터베이스 관리를 위한 시스템인 MySQL, MariaDB, Oracle 등 이 있다면
로컬에서 사용하는 경량 데이터베이스가 있는데요 대표적으로 SQLite가 있습니다.
안드로이드에서 사용하는 로컬 데이터베이스가 바로 SQLite 입니다.
공식문서를 보면 다음과 같은 주의 사항이 있습니다.
https://developer.android.com/training/data-storage/sqlite?hl=ko
안드로이드 측에서는 Room의 사용을 적극적으로 권장하고 있습니다.
그러면 Room은 뭐지?
정식 명칭은 Room Persistence Library
Room은 SQLite에 대한 추상화 레이어를 제공하여 원활한 데이터베이스 액세스를 지원하는 동시에 SQLite를 완벽히 활용합니다.
Room의 구성요소를 알아보고 바로 만들어 보겠습니다.
- Database (데이터베이스)
저장하는 데이터의 집합 단위를 말합니다
- Entity (항목)
데이터베이스 내의 테이블을 의미합니다
- DAO (다오)
데이터베이스에 접근하는 함수(insert,update,delete,...)를 제공합니다
먼저 gradle에 Room을 추가해 줍니다.
app/build.gradle
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"
}
Entity 만들기
@Entity 어노테이션을 class위에 추가합니다
Entity의 테이블 이름을 tb_contacts로 지정해 줍니다.
@PrimaryKey 어노테이션과 autoGenerate=true를 이용해서 Contacts가 새로 생길때마다 id를 자동으로 올라가게 만들어줍니다.
이외에도 이름과 전화번호를 생성자 파라매터로 넣어줍니다.
Contacts.kt
package com.example.mylistapplication
import androidx.room.*
@Entity(tableName = "tb_contacts")
data class Contacts(
@PrimaryKey(autoGenerate = true) val id:Long,
var name: String,
var tel: String
)
DAO 만들기
테이블인 Entity Contacts를 쿼리로 접근하는 인터페이스를 만들어주는 부분입니다
간단히 어노테이션으로만 인서트, 딜리트, 쿼리를 할 수 있습니다
package com.example.mylistapplication
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
@Dao
interface ContactsDao {
@Query("SELECT * FROM tb_contacts")
fun getAll(): List<Contacts>
@Insert
fun insertAll(vararg contacts: Contacts)
@Delete
fun delete(contacts: Contacts)
}
AppDatabase
Room을 실질적으로 구현하는 부분입니다. 우선 RoomDatabase를 상속받습니다.
@Database 어노테이션도 추가해줍니다. Contacts를 사용하며, 버전은 1, 스키마를 추출하지 않음으로 해줍니다.
버전은 추후에 Contacts를 변경할때 migration 할 수 있는 기준이 됩니다. 처음 변경된 적이 없는 처음이니 1 로 해줍니다.
package com.example.mylistapplication
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = [Contacts::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun contactsDao(): ContactsDao
companion object {
private var instance: AppDatabase? = null
@Synchronized
fun getInstance(context: Context): AppDatabase? {
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"database-contacts"
)
.allowMainThreadQueries()
.build()
}
return instance
}
}
}
activity_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/mRecyclerView"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Plus"
android:id="@+id/mPlusButton"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_margin="10dp"/>
</RelativeLayout>
item_contacts.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:id="@+id/mRootView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp">
<TextView
android:id="@+id/mName"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/mTel"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
리싸이클러뷰를 정의해 줍니다.
ContactsListAdapter.kt
package com.example.mylistapplication
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
class ContactsListAdapter(private val itemList : List<Contacts>) : RecyclerView.Adapter<ContactsViewHolder>() {
override fun getItemCount(): Int {
return itemList.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactsViewHolder {
val inflatedView = LayoutInflater.from(parent.context).inflate(R.layout.item_contacts, parent, false)
return ContactsViewHolder(inflatedView)
}
override fun onBindViewHolder(holder: ContactsViewHolder, position: Int) {
val item = itemList[position]
holder.itemView.setOnClickListener {
itemClickListener.onClick(it, position)
}
holder.apply {
bind(item)
}
}
//ClickListener
interface OnItemClickListener {
fun onClick(v: View, position: Int)
}
private lateinit var itemClickListener : OnItemClickListener
fun setItemClickListener(itemClickListener: OnItemClickListener) {
this.itemClickListener = itemClickListener
}
}
ContactsViewHolder.kt
package com.example.mylistapplication
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.item_contacts.view.*
class ContactsViewHolder(v: View) : RecyclerView.ViewHolder(v){
var view : View = v
fun bind(item: Contacts) {
view.mName.text = item.name
view.mTel.text = item.tel
}
}
MainActivity.kt
package com.example.mylistapplication
import android.app.Activity
import android.os.Bundle
import android.util.Log
import android.view.View
import kotlinx.android.synthetic.main.activity_layout.*
import java.util.*
class MainActivity : Activity() {
val TAG = "ListActivity"
var db : AppDatabase? = null
var contactsList = mutableListOf<Contacts>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_layout)
//초기화
db = AppDatabase.getInstance(this)
//이전에 저장한 내용 모두 불러와서 추가하기
val savedContacts = db!!.contactsDao().getAll()
if(savedContacts.isNotEmpty()){
contactsList.addAll(savedContacts)
}
//어댑터, 아이템 클릭 : 아이템 삭제
val adapter = ContactsListAdapter(contactsList)
adapter.setItemClickListener(object : ContactsListAdapter.OnItemClickListener{
override fun onClick(v: View, position: Int) {
val contacts = contactsList[position]
db?.contactsDao()?.delete(contacts = contacts) //DB에서 삭제
contactsList.removeAt(position) //리스트에서 삭제
adapter.notifyDataSetChanged() //리스트뷰 갱신
Log.d(TAG, "remove item($position). name:${contacts.name}")
}
})
mRecyclerView.adapter = adapter
//플러스 버튼 클릭 : 데이터 추가
mPlusButton.setOnClickListener {
//랜덤 번호 만들기
val random = Random()
val numA = random.nextInt(1000)
val numB = random.nextInt(10000)
val numC = random.nextInt(10000)
val rndNumber = String.format("%03d-%04d-%04d",numA,numB,numC)
val contact = Contacts(0, "New $numA", rndNumber) //Contacts 생성
db?.contactsDao()?.insertAll(contact) //DB에 추가
contactsList.add(contact) //리스트 추가
adapter.notifyDataSetChanged() //리스트뷰 갱신
}
}
}
'안드로이드 코틀린' 카테고리의 다른 글
[Kotlin][Android] 권한 요청 기능 만들기 (4) | 2021.08.03 |
---|---|
[Kotlin][Android] 핸드폰 기기 단말 정보 가져오기 (0) | 2021.08.02 |
[Kotlin][Android] 툴바(toolbar) 및 메뉴버튼 만들기 (0) | 2021.07.25 |
[Kotlin][Android] 네트워크 연결 상태 확인 (0) | 2021.07.23 |
[Kotlin][Android] 화면 넘기기 구현하기 (0) | 2021.07.20 |