티스토리 뷰

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
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
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
글 보관함