이번 포스팅은 XML에서 액티비티끼리 데이터를 주고받을 때 사용하는 registerForActivityResult를 Compose에서 사용하는 방법을 공부해 보겠다!
만약 프로젝트 구성을 Compose Only면 필요 없을지도 모르지만 XML + Compose을 혼합해서 사용한다면 Compose -> Acltitly -> 원래 Compose로 데이터를 전달해야 할 경우가 생긴다. 이럴 경우 어떤 식으로 해결하는지 아래 예제로 알아보자!
# 사용법
공식 문서에서 보면 이미지를 가져오는 예제가 있다. 보통 예제는 권한을 확인하거나 공식 문서처럼 이미지를 가져오는 예제이다.
여기서는 간단한 데이터를 가져오는 예제를 할 것이다.
예제의 순서를 설명하자면
1. Composable 함수인 A Screen에서 Activity인 B Activity로 이동
2. B Activity에서 A Screen로 값 전달
이렇게 하면 예제는 끝난다.

## A Screen.kt (Composable)
@Composable
fun AScreen(){
var userName by remember {
mutableStateOf("")
}
var userAge by remember {
mutableStateOf("")
}
//// rememberLauncherForActivityResult로 생성
val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) { result ->
//// 넘어온 값이 RESULT_OK이면 getStringExtra로 값 가져오기
if (result.resultCode == Activity.RESULT_OK) {
val data = result.data
userName = data?.getStringExtra("NAME")?:""
userAge = data?.getStringExtra("AGE")?:""
}
}
val context = LocalContext.current
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
){
Text(
text = "유저 이름은? ${userName.ifBlank {"빈값"}}",
fontSize = 24.sp,
fontWeight = FontWeight.W700,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(20.dp))
Text(
text = "유저 나이는? ${userAge.ifBlank {"빈값"}}",
fontSize = 24.sp,
fontWeight = FontWeight.W700,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = {
//// 위에서 만든 launcher를 이용해 launch후 실행할 Intent를 만들어준다.
launcher.launch(Intent(context, BActivity::class.java))
}) {
Text(
text = "B액티비티로 이동"
)
}
}
}
## B Activity.kt
class BActivity : AppCompatActivity() {
private lateinit var binding: ActivityBactivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityBactivityBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnSave.setOnClickListener {
val resultIntent = Intent()
val name = binding.inputName.text.toString()
val age = binding.inputAge.text.toString()
//// 위에서 가져온 Intent를 이용해 putExtra로 값을 보냄
resultIntent.putExtra("NAME",name)
resultIntent.putExtra("AGE",age)
//// setResult를 사용해 RESULT_OK 코드와 만든 resultIntent를 넘겨준다
setResult(Activity.RESULT_OK, resultIntent)
finish()
}
}
}
위는 전체적인 코드이다 이 코드에서 볼 것은 주석 부분을 보면 된다. 이제 하나씩 살펴보자!
##1단계 Composable에서 rememberLauncherForActivityResult을 생성
액티비티끼리 통신에서는 registerForActivityResult를 사용했는데 위에서 설명한
rememberLauncherForActivityResults는 컴포즈용 registerForActivityResult라고 보면 된다.
이제 rememberLauncherForActivityResults가 뭔지 알아보자!

우선 첫 번째 인자로는 공식문서의 사진으로 보시는 거와 같이 ActivityResultContract 객체를 넣어야 한다.
※ ActivityResultContract객체로는 아래 사진과 같이 공식문서 가면 나와있으니 참조 바람

우리는 저 많은 객체 중 ActivityResultContracts.StartActivityForResult()를 사용할 것이다. 그래서 첫 번째 인자로는 ActivityResultContracts.StartActivityForResult() 넣어주고, 두 번째 인자로는 onResult라고 반환함수가 들어온다 이 부분은 이제 우리가 익숙하던 방식으로 getStringExtra, getIntExtra로 값을 가져와 처리해주면 된다.
참고로 넘어온 값은 onResult에 반환된 값. data후 getStringXXX를 해줘야 하는 점을 기억해야 한다.
##2단계 rememberLauncherForActivityResult로 만든 객체를 launch후 실행할 Intent를 만들어준다
Button(onClick = {
//// 위에서 만든 launcher를 이용해 launch후 실행할 Intent를 만들어준다.
launcher.launch(Intent(context, BActivity::class.java))
}) {
Text(
text = "B액티비티로 이동"
)
}
위 예제에서는 rememberLauncherForActivityResult로 만든 객체이름을 launcher로 해줬기 때문에 launch를 해준다.
첫 번째 인자로 실행하고 싶은 액티비티를 인텐트로 만들어서 넣어 준다!
이러면 2단계 작업은 끝이다.
##3단계 넘어간 액티비티에서 하고 싶은 작업이 끝나면 Intent를 가져온 후 putExtra를 이용해 값을 넘겨주고 setResult를 사용
binding.btnSave.setOnClickListener {
val resultIntent = Intent()
val name = binding.inputName.text.toString()
val age = binding.inputAge.text.toString()
//// 위에서 가져온 Intent를 이용해 putExtra로 값을 보냄
resultIntent.putExtra("NAME",name)
resultIntent.putExtra("AGE",age)
//// setResult를 사용해 RESULT_OK 코드와 만든 resultIntent를 넘겨준다
setResult(Activity.RESULT_OK, resultIntent)
finish()
}
위 예제에서는 btnSave라는 버튼을 클릭하면 name, age해당 EditText에 있는 text를 기존 액티비티끼리 값 전달 하는 것처럼 putExtra와 setResult를 사용해 넘기면 된다.
여기서 기존 액티비티는 어떤 식으로 처리하는지가 궁금하다면 클릭하길 바란다!
##4단계 돌아온 Composable 함수에서 1단계의 rememberLauncherForActivityResult의 콜백으로 넘어온 값을 확인
val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) { result ->
//// 넘어온 값이 RESULT_OK이면 getStringExtra로 값 가져오기
if (result.resultCode == Activity.RESULT_OK) {
val data = result.data
userName = data?.getStringExtra("NAME")?:""
userAge = data?.getStringExtra("AGE")?:""
}
}
이 예제에서는 넘어온 콜백 값 중 resultCode가 RESULT_OK이면 result의 data를 호출해 getStringExtra로 가져오는 로직이다!
그래서 이렇게 모든 단계를 차례대로 하다 보면 값 전달이 된다!