오늘은 앱개발 과제에서 새롭게 알게 된 메서드 registerForActivityResult에 대해 리뷰해보려 한다.
기존에 액티비티 흐름에서 A(B call) -> B(result send) -> A(result receive) 구조를 짜려면 startActivityForResult를 사용했다.
하지만, 지금은 startActivityForResult가 Deprecated됐다.
그 이유는 A에서 새롭게 시작된 Some 액티비티에서 메모리를 많이 사용할 경우 이전에 열려있던 A 액티비티가 다운되버려서
제대로 callBack을 받지 못할 때가 있었기 때문이다.
이를 해결하기 위해서 새로운 Some 액티비티를 실행시키는 부분과 callBack을 관리하는 부분을 분리했다.
-> 이렇게 탄생한게 registerForActivityResult이다.
한 줄 정리 : 이전 방식은 callBack이 제대로 되지 않는 case가 있어 삭제됐고, registerForActivityResult가 도입됨
기존의(startActivityForResult) Sample Code
// A Activity
fun test() {
val intent = Intent(this, SomeActivity::class.java)
startActivityForResult(intent, RESULT_OK)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK) {
val userData = data?.let { it.getStringExtra("user") } ?:
throw NullPointerException("~")
}
}
// Some Activity
fun result() {
// 종료 시, 이전 Activity에 데이터를 넘기는 방법 :
// 1. intent에 putExtra를 통해 key와 value를 심는다
intent.putExtra("userID", editTextID.text.toString())
intent.putExtra("userPW", editTextPW.text.toString())
// 2. setResult에 종료 코드와 key, value를 심은 intent를 넣어준다.
setResult(RESULT_OK, intent)
finish()
}
변경된(registerForActivityResult) Sample Code
// A Activity
// registerForActivityResult는 전역에 선언하고 정의해야 한다.(난 class 파라미터로 넣음)
private val resultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode != Activity.RESULT_OK) {
return@registerForActivityResult
}
when {
!result.data?.getStringExtra("data").isNullOrBlank() -> {
val resultData = result.data?.getStringExtra("userID") // 전달 받은 String 데이터
}
}
fun test() {
val intent = Intent(this, SomeActivity::class.java)
resultLauncher.launch(intent) // startActivityForResult(intent, RESULT_OK)
}
// Some Activity <- 이전 코드와 같음
fun result() {
// 종료 시, 이전 Activity에 데이터를 넘기는 방법 :
// 1. intent에 putExtra를 통해 key와 value를 심는다
intent.putExtra("userID", editTextID.text.toString())
// 2. setResult에 종료 코드와 key, value를 심은 intent를 넣어준다.
setResult(RESULT_OK, intent)
finish()
}
우선 if문에서 result.resultCode는 어떤 액티비티로 부터 callback이 된건지 명시적으로 체크하기 위해서 사용하는데
현재는 A와 Some 액티비티로 단순한 구조여서 RESULT_OK가 아니면 return 하도록 구현했지만.
만약 case에 따라 (B~E)중 하나의 액티비티로부터 값을 반환 받아야 한다면 when문으로 작성해야 할 것이다.
그리고 좀 더 들어간다면 B라는 액티비티에서 조건에 따라 callback Data Type이 다를 수 있으므로 아래와 같이 구성할 수 있을 것 같다.
- 아래의 두 코드는 검증된 코드가 아닙니다.(안 돌아갈 수 있음) 단순히 상황에 따라 어떻게 코드를 처리하면 좋을지 생각해보는 과정에서 해결 방법을 코드로 시각화한 것이라고 생각해주세요.
val B = 101
val C = 102
private val resultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
when (result.resultCode) {
B -> {
when {
result.data?.getStringExtra("data").isNullOrBlank().not() -> {
// "data"를 키 값으로 string data를 가져와 처리하는 로직
}
result.data?.getStringExtra("user").isNullOrBlank().not() -> {
// "user"를 키 값으로 String data를 가져와 처리하는 로직
}
}
}
C -> {
result.data?.getBooleanExtra("isPass").isNullOrBlank().not() -> {
// "isPass"를 키 값으로 Boolean data를 가져와 처리하는 로직
}
}
}
}
또, 하나의 아이디어는 result.resultCode로 관리할 정도로 복잡한 로직이 아닐 때, (B~E) 액티비티가 모두 RESULT_OK 코드를 사용하도록 한 뒤에.
B에서는 "data"를 키값으로 하는 value를 반환하도록 / C에서는 "isPass"를 키값으로 하는 value를 반환하도록 해서 아래와 같이 짜도 될 것 같다.
private val resultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode != Activity.RESULT_OK) {
return@registerForActivityResult
}
when {
result.data?.getStringExtra("data").isNullOrBlank().not() -> { // B Activity인 경우
// "data"를 키 값으로 string data를 가져와 처리하는 로직
}
result.data?.getBooleanExtra("isPass").isNullOrBlank().not() -> { // C Activity인 경우
// "isPass"를 키 값으로 Boolean data를 가져와 처리하는 로직
}
}
}
관련 공식 문서 ref : https://developer.android.com/training/basics/intents/result?hl=ko
'Android' 카테고리의 다른 글
[학습 1장] findViewById / Kotlin Extensions / ViewBinding / DataBinding 정리 (0) | 2024.03.28 |
---|---|
[docs] 앱 아키텍처 가이드 읽어보기 (1) | 2024.03.22 |
안드로이드 앱개발 입문 1~4주차 학습 (0) | 2024.03.18 |
ViewPager2 사용해서 반복되는 화면 작업하기 (0) | 2024.02.20 |
많이 사용했지만, 까먹는 단축키 (1) | 2024.02.16 |