[Kotlin] 안드로이드 코틀린 연습
- [ Languages ]/Kotlin
- 2022. 7. 6.
를 보고 연습해보기..
+ developer 사이트 뒤지는거 너무 번거롭다
+ 버전오류도 엄청 많이나는것같음 (unresolved나 메소드 못찾는 등 오류 뜨면 거의 버전오류임)
1. Room DB를 이용하여 데이터 저장하기.
안드로이드 공식사이트(developer.android.com)를 참조해보면 ROOM DB 사용을 위해서 가장 먼저 build.gradle을 수정해주자
//room 사용
// val roomVersion = "2.4.2"
implementation("androidx.room:room-runtime:2.4.2")
annotationProcessor("androidx.room:room-compiler:2.4.2")
// To use Kotlin annotation processing tool (kapt)
kapt("androidx.room:room-compiler:2.4.2")
DB 실습을 위해서 xml뷰 하나와 코틀린 클래스 파일들을 만든다. MainActivity는 데이터를 서버 DB에서 뷰로 불러와주는 코드가 존재해야 하는데, 이를 위해서 Todo라는 엔티티 클래스를 만들고 TodoDAO라는 DB접근을 위한 DAO클래스도 만들어준다. 마지막으로 AppDataBase라는 추상 클래스에 todoDAO를 @Database어노테이션을 이용하여 등록해주면 된다.
그 후에 MainActivity에서
val db = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java, "database-name"
).allowMainThreadQueries()
.build()
> Room DB를 코드로 활용할 수 있게 하기 위해서 db라는 접근변수를 만들어준다.
//처음에 껐다가 키면 데이터를 블러와서 뿌림
result_text.text = db.todoDAO().getAll().toString();
add_button.setOnClickListener {
db.todoDAO().insert(Todo(todo_edit.text.toString()))
result_text.text = db.todoDAO().getAll().toString();
}
> result_text라는 id를 가진 뷰에 db.todoDAO().getAll().toString()을 이용하여 뿌려주고, 추가 버튼의 click이벤트가 발생할때마다 todoDAO().insert()를 통해서 새로운 정보를 db에 추가해주고 다시 db.todoDAO().getAll().toString()을 사용하여 뿌려주는 방식으로 동작하는 코드이다.
2. LiveData 활용
1번에서 사용한 방법은 처음에 화면을 갱신하고, click 이벤트가 발생할때마다 새로 코드를 불러와서 화면을 갱신해주는 방법으로 구현하였다. 하지만 이런 식으로 사용하게되면 업데이트를 하거나, 다양한 상황에서 화면을 불러오는 코드를 이용하여 화면갱신을 해야하기 때문에 번거롭거나 활용성이 떨어질 수 있다. 따라서 LiveData를 활용하여 실시간으로 DB가 반영되는 코드를 짜보자.
db.todoDAO().getAll().observe(this, object: Observer<List<Todo>>{
override fun onChanged(todos: List<Todo>?) {
result_text.text = todos.toString();
}
})
> UI를 갱신해주는 코드. db의 getAll()은 LiveData<List<Todo>>형태로 Todo리스트를 제네릭으로 갖는 LiveData형을 반환하는데, 여기 내장되어있는 observe함수를 오버라이딩하고 Observer객체를 인자로 넣어준다. 이 때 onChanged라는 함수를 오버라이딩해주는데, 이 코드는 LiveData가 DB의 변화를 감지할때마다 result_text라는 뷰에 DB의 항목을 갱신해줌을 의미한다.
//버튼 클릭시 DB에 insert
add_button.setOnClickListener {
db.todoDAO().insert(Todo(todo_edit.text.toString()))
// result_text.text = db.todoDAO().getAll().toString(); //갱신
}
> 1번 방법과 동일하게 버튼을 클릭할 때 DB에 insert를 하는 코드이지만, 1번과는 다르게 onClick함수에 갱신하는 로직을 넣지 않고 위에 표시된 Observer객체를 이용한다.
3. 비동기 처리
insert나 update, delete등을 한번에 대량으로 하는 케이스와 같은 경우 성능의 저하가 일어날 수 있기 때문에 구글에서도 백그라운드에서 DB access를 하는것을 권고하고 있기 때문에 로직을 바꾸어보도록 하자
val db = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java, "database-name"
).allowMainThreadQueries() //메인쓰레드 위에서 동작
.build()
자바에서는 AsyncTask와 같은 방법을 사용할 수도 있지만, 코틀린에서는 Coroutine의 LifecycleScope를 이용해보도록 하자. developer.android.com에서 LifecycleScope에 관련된 문서를 검색하고(https://developer.android.com/topic/libraries/architecture/coroutines), gradle에 구성요소 추가해준다음
//버튼 클릭시 DB에 insert
add_button.setOnClickListener {
//이 코드 안에서 돌아가는 로직은 모두 Background에서 동작
lifecycleScope.launch(Dispatchers.IO) {
db.todoDAO().insert(Todo(todo_edit.text.toString()))
}
}
onclickListener부분에 lifecycleScope.launch() {} 를 이용하여 코드를 구성하자. 이 코드 내에서 돌아가는 로직은 모두 Background에서 동작하도록 설정된다.
4. UI와 로직 분리-ViewModel
이전 코드에서는 UI(User Interface)와 로직이 같이 혼합되어 있는 형태로 구성되어있었다. 대신 이번에는 ViewModel을 사용하여 UI와 로직을 분리해보자.
일반적으로 안드로이드에서 화면을 전환하면 화면이 파괴(onDestroy)되고 다시 생성(onCreate)된다. 하지만 ViewModel은 액티비티가 완전히 종료될때까지 데이터들을 유지하고 있기 때문에 ViewModel을 사용해보자.
가장 먼저 ViewModel 클래스를 만들자. 기존에 MainActivity 클래스에 존재하는 코드들에서 로직을 담당하는 부분을 뷰모델로 옮겨올 것이다.
class MainViewModel(application:Application):AndroidViewModel(application) {
private val db = Room.databaseBuilder(
application,
Appdatabase::class.java, "database-name"
).build()
fun getAll(): LiveData<List<Todo>> {
return db.todoDAO().getAll()
}
suspend fun insert(todo:Todo) {
db.todoDAO().insert(todo)
}
}
db에 접근하는 변수와 getAll(), insert()과 같은 로직함수를 뷰모델로 분리해온 후, 생성자를 private으로 지정해서 외부에서 메소드로만 접근 가능하게 하였다. 이때 ViewModel클래스는 AndroidViewmodel을 상속받고, application을 인자로 받는다. 이것은 MainActivity에서 viewModel변수로 접근할 때 activity를 넘겨줄 수 있다.(this로)
val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
//UI 갱신
viewModel.getAll().observe(this, object: Observer<List<Todo>>{
override fun onChanged(todos: List<Todo>?) {
result_text.text = todos.toString();
}
})
//버튼 클릭시 DB에 insert
add_button.setOnClickListener {
//이 코드 안에서 돌아가는 로직은 모두 Background에서 동작
lifecycleScope.launch(Dispatchers.IO) {
viewModel.insert(Todo(todo_edit.text.toString()))
}
}
> MainActivity의 일부이다. viewModel 객체를 선언하고, 기존에 db를 이용하여 직접 접근하던 부분을 ViewModel클래스의 메소드를 이용하여 간접적으로 사용하는 것을 볼 수 있다.
5. DataBinding
안드로이드 데이터바인딩은 간단하게 xml파일에 데이터들을 연결해서 사용할 수 있게 도와주는 기능을 의미하며, 기존에사용했던 'UI와 데이터를 프로그램적 로직(onCreate()와 같은 함수)으로 연결하지 않고, 레이아웃 파일에서 직접 선언적 방식으로 결합하게 도와주는 라이브러리'를 의미한다, MVVM(Model-View-View Model)패턴에서 LiveData와 함께 거의 필수적으로 사용된다.
Data Binding 참고 : https://developer.android.com/topic/libraries/data-binding
먼저 gradle에 databinding관련 설정정보를 추가한 후, MainActivity를 수정해준다.
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.lifecycleOwner = this //LiveData 관찰을 위해
val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
binding.viewModel = viewModel
기존 setContentView를 DataBindingUtil의 setContentView로 교체해주고, LiveData 관찰을 위해서 binding.lifecycleOwner를 현재 activity로 설정해준다. 그리고 4번에서 사용한 뷰모델 객체를 DatabindingUtil의 viewModel로 교체해주면 된다. 기존에 사용하던 로직들을 DataBinding이라는 클래스 안으로 갖고 들어오는 것
그 다음으로는 xml파일에 존재하는 기존 태그들을 <layout></layout>태그로 감싼 후, <data></data>태그도 추가한다. 이 <data>태그는 <layout>내에서 사용할 변수를 정의하는데 사용한다. 즉, 밑의 예제는 ViewModel이라는 객체를 xml에서 변수로 사용할 것임을 의미한다.
<data>
<variable
name="viewModel"
type="com.example.room_kotlin.MainViewModel" />
</data>
이런식으로 데이터 태그로 추가해준다.
이제는 xml 레이아웃 내에서 @{}구문을 사용하여 직접 변수를 활용하면 된다. viewModel 변수의 사용을 위해서 먼저 ViewModel 클래스에 변수를 선언해둔 뒤에, 활용해주면 된다 (뭔가 thymeleaf 데이터 바인딩과 비슷한 느낌이다)
var todos: LiveData<List<Todo>>
init {
todos = getAll()
}
android:text="@{viewModel.todos.toString()}"
그렇다면 왜 굳이 데이터 바인딩을 사용하는 걸까? 데이터 바인딩을 사용하면, 데이터를 UI요소에 연결하기 위한 코드를 분배할 수 있다. 이는 적절하게 사용하면 코드량과 가독성 측면에서 이득을 볼 수 있음을 의미한다.
'[ Languages ] > Kotlin' 카테고리의 다른 글
[Kotlin] 자바 대신 코틀린 사용하기 (0) | 2023.09.01 |
---|---|
[Kotlin] 기본 문법(2) - 심화 (0) | 2022.07.02 |
[Kotlin] 기본 문법(1) (0) | 2022.06.30 |