티스토리 뷰
startActivityForResult를 사용할 때, 아래처럼 바로 결과를 받아오면 코드가 더 간결하고 읽기 쉬워질 거라는 생각을 여러번 했었다.
val intent = Intent(this, AnotherActivity::class.java)
val activityResult = startActivityForResult(intent)
if (activityResult.resultCode = Activity.RESULT_OK) {
//doSomething()
}
Coroutines에서 지원하는 CompletableDeffered란 녀석을 이용하면 위와 같은 순차적인 코드가 가능해 진다.
Activty에 다음 코드를 추가한다.
class DefferedActivity: AppCompatActivity() {
internal var deffered = CompletableDeffered<ActivityResult>()
}
data class ActivityResult(val resultCode: Int, val data: Intent?)
Deffered 는 kotlinx.coroutines 패키지에 포함되어 있고, non-bocking이며 취소가 가능한 future이며, 아래와 같이 정의된 interface 이다. Future는 말 그대로 앞으로 결과를 받을 수 있는 것으로, callback이나 promise와 비슷한 것이라고 이해하면 쉬울 것 같다.
interface Deferred<out T> : Job (source)
CompletableDeffered는 Deffered를 구현한 클래스로, public function인 complete or cancel 을 통하여 일을 마치도록 되어 있다. 이 녀석을 이용하면 startActivityForResult로부터 결과가 올 때까지 thread 를 방해하지 않고 결과를 기다리게 할 수 있다.
아래와 같이 startForResultAsync 함수를 추가한다.
class DefferedActivity: AppCompatActivity() {
companion object {
private const val RC_ACTIVITY_FOR_RESULT = 101
}
internal var deffered = CompletableDeffered<ActivityResult>()
private fun startForResultAsync(intent: Intent): Deffered<ActivityResult> {
if (deffered.isActive) deffered.cancel()
deffered = CompletableDeffered<ActivityResult>()
startActivityForResult(intent, RC_ACTIVITY_FOR_RESULT)
return deffered
}
}
onActiviryResult를 override해서 결과를 받는다.
override fun onActivityResult((requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RC_ACTIVITY_FOR_RESULT) {
deffered.completed(requestCode = requestCode, data = data)
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
requestCode가 우리가 요청했던 코드이면 deffered 를 완료하면 되고, 아니면 기존 대로 처리한다.
그럼, 이 기능을 어떻게 사용하는 살펴보자.
val mainScope: CoroutineScope by lazy { MainScope() }
override fun onCreate(savedInstance: Bundle?) {
val button = findViewById(R.id.button)
button.setOnButtonClick {
mainScope.launch {
val intent = Intent(this, AnotherActivity::class.java)
val activityResult = startForResultAsync(intent).await()
if (activityResult.resultCode == Activity.RESULT_OK) {
//do something
}
}
}
}
Acitivity에서 Coroutines를 사용하기 위해서는 CoroutinScope이 필요하다. mainScope을 멤버를 선언하고 안드로이드 kotlinx.coroutines의 MainScope()를 이용해서 CoroutinScope을 생성한다.
위의 함수를 Base Activity에 구현하면 다른 Activity들에서도 쉽게 이용할 수 있을 것이다. 더 나아가 extension function 형태로 함수를 제공하는 것도 고려해 볼만하다.
fun DefferedActivity.startForResult(intent: Intent, callback: (ActivityResult) -> Unit = {}) {
mainScope.launch {
callback(startForResultAsync(intent))
}
}
이제 앞서의 사용법이 아래처럼 바뀌게 된다.
override fun onCreate(savedInstance: Bundle?) {
val button = findViewById(R.id.button)
button.setOnButtonClick {
mainScope.launch {
val intent = Intent(this, AnotherActivity::class.java)
startForResult(intent) {
if (activityResult.resultCode == Activity.RESULT_OK) {
//do something
}
}
}
}
}
훨씬 읽기 쉽고 깔끔한 코드가 완성되었다.
'Kotlin > Coroutines' 카테고리의 다른 글
안드로이드 쓰레드 예제 몇가지 (0) | 2021.03.24 |
---|---|
Coroutines는 생각만큼 간단하지 않다 (0) | 2021.03.15 |