-
일반적으로 잘 사용하는 아래의 패턴은 꽤 나이스하게 바뀐다.
onPreExecute 에서 progress
doInBackground 에서 bg job
onPostExecute 에서 progress 닫고 UI 작업
-
// AsyncTask
object : AsyncTask<Void, Void, List<MyAccountItem>>() {
override fun onPreExecute() {
showProgress(R.string.loading)
}
override fun doInBackground(vararg params: Void): List<MyAccountItem> {
return getMyAccountItemList();
}
override fun onPostExecute(result: List<MyAccountItem>) {
if (isFinishing) return
hideProgress()
updateUI(result)
}
}.execute()
// Coroutine
GlobalScope.launch(coroutineContext) {
showProgress(R.string.loading)
val accountList = async(Dispatchers.Default) { getMyAccountItemList() }.await()
if (isFinishing) return@launch
hideProgress()
refreshUI(accountList)
}내가 본 장점들은 이렇다.
1. override 함수들이 제거되어 코드가 짧아진다.
2. coroutine 은 android 종속이 아니라 kotlin 코드를 사용하는 모든 곳에 share 가능한 형태의 코드가 된다.
3. flow 가 더 잘 보인다. (override 함수들의 경우 위치에 따라 따라가기가 어려울 수 있다.)
4. return type 에 대한 변경이 훨씬 자유롭다.
5. shared variable 들의 범위가 local 로 한정된다. 줄어든다. (케바케겠지만..)
6. kotlin 과 함께 val 을 쓰기도 좋고, nullable check 등이 더 유려해진다.
-
그러나.. onCancel 을 처리해야 하는 과정에서는 기존 AsyncTask 에 친숙한 사람들 기준에서 볼 때 가독성 측면에서 불편함이 생긴다.
// AsyncTask
private inner class DeleteContactsTask(private val mAccountItem: MyAccountItem) : AsyncTask<Void, Int, Void>() {
override fun onPreExecute() {
showProgress(R.string.deleting){
cancel(true)
}
}
override fun onCancelled(result: Void?) {
hideProgress()
showToast(R.string.toast_msg_delete_canceled)
refreshUI()
}
override fun onProgressUpdate(vararg values: Int?) {
val deletedCount = values[0]!!
increaseProgress(deletedCount)
}
override fun doInBackground(vararg params: Void): Void? {
ContactsDBHelper.deleteAllContacts(mAccount) { deletedCount ->
publishProgress(deletedCount)
}
ContactsDBHelper.deleteContacts(
account = mAccountItem.account,
deletedCallback = { deletedCount -> publishProgress(deletedCount) },
tackCancelWatcher = { return isCancelled() }
)
return null
}
override fun onPostExecute(result: Void?) {
if (isFinishing) return
hideProgress()
showToast(R.string.toast_msg_delete_success)
refreshUI()
}
}
// Coroutine
GlobalScope.launch(coroutineContext) {
val job = launch(Dispatchers.Default) {
ContactsDBHelper.deleteContacts(
account = myAccount.account,
progressListener = { deletedCount ->
launch(coroutineContext){}
increaseProgress(deletedCount)
}
},
taskCancelWatcher = { isActive == false )
)
}
showProgress(R.string.deleting) {
job.cancel()
}
job.join()
if (isFinishing) return@launch
hideProgress()
if (job.isCancelled) {
showToast(R.string.toast_msg_delete_canceled)
} else {
showToast(R.string.toast_msg_delete_success)
}
refreshUI()
}장점은 앞선 것과 거의 동일하며, 추가적인 내용은 onCancelled 와 onPostExecute 에서 공통 로직으로 처리해야 하는 것들을 함께 쓸 수 있다는 것이 좋다.
단점들이 보이는데..
1. Job 을 cancel 해야 하기 때문에, showProgress 가 뒤로 밀리게 되어 일반적인 AsyncTask 를 쓰는 것에 비해 가독성이 조금 떨어질 수 있다.
2. cancel, join, CancellationException 등의 관계를 잘 알지 못하면 혼란스러울 수 있다.
단점 중 2번은 학습을 통해 어차피 알아야 하는 것이고, 1번이 약간 그렇지만..
장점을 상쇄하고 남는것 같다.
-
끝!
'프로그래밍 놀이터 > Kotlin, Coroutine' 카테고리의 다른 글
| [coroutine] Exception handling (0) | 2019.03.04 |
|---|---|
| [Coroutine] Coroutine context and dispatchers (4) | 2019.02.08 |
| [도서 정리] Android Development with Kotlin - Delegates (0) | 2018.12.17 |
| [도서 정리] Android Development with Kotlin - Extension Functions and Properties (0) | 2018.12.16 |
| [도서 정리] Android Development with Kotlin - Generics Are Your Friends (0) | 2018.12.15 |
댓글