안드로이드 프로그래밍 책을 읽으면서 실습을 진행하던 도중 startActivityForResult가 deprecated 됐다고 IDE 경고가 떴습니다.
무엇으로 대체하여 사용하는지 궁금해서 공식 문서를 뒤져봤습니다.
공식 안드로이드 문서에서는 AndroidX Activity와 Fragment에 도입된 Activity Result API 사용을 적극 권장한다고 합니다.
따라서 두 가지 API를 모두 사용해보고 왜 startActivityForResult에서 registerForActivityResult로 변화했는지 생각해보는 시간을 가져보았습니다.
public void startActivityForResult(Intent intent,int requestCode)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
이 함수는 onActivityResult 함수와 짝으로 사용합니다. startActivityForResult를 통해 시작된 액티비티가 세팅한 결과를 onActivityResult에서 받을 수 있습니다.
간단한 예제를 살펴보겠습니다.
아래의 코드는 button을 누르면 갤러리를 실행해서 사진파일을 결과로 가져온 후 이미지 뷰에 띄워주는 예제입니다.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.i("lifecycle", "onCreate called")
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.button.setOnClickListener {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
startActivityForResult(intent, 110)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 110 && resultCode == RESULT_OK) {
val uri = data?.data as Uri
val bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, uri)
binding.imageView.setImageBitmap(bitmap)
Toast.makeText(this, "success", Toast.LENGTH_SHORT).show()
}
}
}
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull ActivityResultContract<I, O> contract,
@NonNull ActivityResultCallback<O> callback)
registerForActivityResult 함수는 ActivityResultContract와 ActivityResultCallback을 인자로 받습니다.
ActivityResultContract는 registerForActivityResult에서 반환된 ActivityResultLauncher가 어떤 행동을 할지를 결정하는 역할을 합니다.
우리는 새로운 액티비티를 실행하고 그 액티비티에서 결과를 받아오는 작업을 할 것이므로 ActivityResultContracts.StartActivityForResult()를 첫번째 인자로 사용합니다.
두번째 인자는 onActivityResult만을 추상 메서드로 갖는 인터페이스 입니다. SAM 인터페이스이므로 그냥 람다표현식을 사용해서 초기화 할 수 있습니다.
이제 registerForActivityResult를 사용하여 예제를 만들어보겠습니다.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.i("lifecycle", "onCreate called")
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val activityLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
val data = it.data?.data as Uri
val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, data)
binding.imageView.setImageBitmap(bitmap)
}
}
binding.button.setOnClickListener {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
activityLauncher.launch(intent)
}
}
startActivityForResult를 사용하면 함수와 콜백이 멀리 떨어져 있어서 코드가 좀 길어지면 함수를 찾아서 왔다갔다 하느라 좀 불편할 것 같았습니다.
또 registerForActivityResult에서 다양한 contract를 사용하면 해당 작업들에 대해 권한 작업이나 여타 자질구레한 작업들을 contract에서 처리해준다는 사실도 검색을 하며 알게되었습니다.
되도록이면 registerForActivityResult를 사용하자!