-
일반적으로 잘 사용하는 아래의 패턴은 꽤 나이스하게 바뀐다.
onPreExecute 에서 progress
doInBackground 에서 bg job
onPostExecute 에서 progress 닫고 UI 작업
-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // 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 에 친숙한 사람들 기준에서 볼 때 가독성 측면에서 불편함이 생긴다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | // 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 |
댓글