[Android] registerForActivityResult

heetaeheo·2022년 5월 10일
1

Android

목록 보기
12/12
post-thumbnail

registerForActivityResult

해당 메소드를 처음 접한 것은 팀 프로젝트(위드 마켓)을 개발하면서 알게되었습니다.
사용자 프로필 이미지를 변경하기 위해 사용자 스마트폰 내부 저장소에 접근이 필요했고 그때 Intent와 onActivityResult를 사용하여 사용자 스마트폰의 저장소에서 사진을 선택하고 Bitmap으로 매핑하였습니다.

해당 코드를 가지고 주간 진행상황을 발표하고 코드리뷰를 기간을 한 주(a week)가지게되었습니다. 같이 개발을 하고 있는 팀원이 저에게 결과를 받아올때 onActivityResult를 사용하던데 registerForActivityResult를 한번 공부해보라고 하였습니다.

알고보니 onActivityResult와 함께 쓰이는 startActivityForResult가 deprecated되었고 동시에 onActivityResult도 사용이 멈춘 것입니다. 그러고 나온 새로운 메서드가 registerForActivityResult인 것입니다.

그렇다면 왜 startActivityForResult()가 deprecated되었을까?

startActivityForResult()의 deprecated 된 이유

  1. AndroidX Activity, Fragment에 도입된 Activity Result API 사용의 권장

  2. onActivityResult를 사용할 때 Memory leak으로 인한 프로세스와 Activity의 소멸 가능성
    -> 기존 Activity에서 startActivityResult를 통해서 콜백을 등록하고, onActivityResult에서 콜백을 처리합니다. 두 메서드 같은 곳에서 구현을 해야하는 데 Memory leak으로 인해 제대로 된 동작을 안할 수 있다는 것입니다.

기존 API

  • startActivityForResult(Intent intent, int requestCode,Bundle options)
    : 새로운 Activity 시작 + 결과값 전달

    -> startActivityForResult는 requestCode를 이용하여 어떤 Activity인지 식별을 하는 것입니다. 즉 Activity가 많아지고 requestCode또한 증가한다면 해당 결과값을 받아오는 onActivityResult에서는 해당 requestCode의 값을 확인하고 해당 경우의 수 만큼의 코드를 작성해야하는 즉 보일러 플레이트가 많아지게 된다는 것입니다.

새로운 API

Activity Result API는 다른 Activity를 실행하는 코드에서 결과 콜백을 분리한다.
Result Callback은 프로세스와 Activity를 다시 생성할 때 사용할 수 있어야 하므로 다른 Activity를 실행하는 로직이 사용자 입력 또는 기타 비즈니스 로직을 기반으로 발생하더라도 Activity가 생성될 때 마다 콜백을 무조건 등록해야 한다.

registerForActivityResult()는 ActivityResultContract 및 ActivityResultCallback을 가져와서 다른 Activity를 실행하는데 사용할 ActivityResultLauncher를 반환한다.

새로운 API에는 requestCode가 없어졌는데 어떻게 구동될까?

원하는 Activity Request마다 registerForActivityResult를 실행하여 Result Callback을 분리해서 구현하므로 Activity를 구분할 필요가 없어졌기 때문입니다.

현재 프로젝트에서 사용된 코드를 가지고와 설명을 드리겠습니다.

코드 비교

startActivityForResult.kt

    private fun loadImage() {
        var intent_image = Intent()
        intent_image.type = "image/*"
        intent_image.action = Intent.ACTION_GET_CONTENT
startActivityForResult(Intent.createChooser(intent_image,"Load Picture"),galley)
    }

startActivityForResult에서 loadImage라는 함수안에서 동작을 시작합니다. 사용자의 스마트폰 내장 저장소에서 이미지 파일을 가지고 오기 위해 동작을 실행합니다.

onActivityResult.kt

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if(galley == requestCode){
            if(resultCode == RESULT_OK){
                val dataUri : Uri? = data?.data
                try {
                    val bitmap : Bitmap = MediaStore.Images.Media.getBitmap(context?.contentResolver,dataUri)
                   binding.profileImage.setImageBitmap(bitmap)
                }  catch (e: Exception) {
                    Toast.makeText(context,"$e",Toast.LENGTH_SHORT).show()
                }
            }
       }
    }

결과값을 받는 onActivityResult에서 해당 requestCode를 받아 해당 requestCode를 확인한 후 해당 이미지를 프로필 이미지로 변환하는 작업을 하게됩니다. 이 상황에서 사용하는 것이 이미지의 변경 하나만 있기때문에 if로 확인하는 작업을 한 번 한 것이지 여러개라면 그 수에 맞게 계속 작업해줘야한다는 것입니다. 또한 어떠한 Activity는 requestCode가 10, 다른 Activity는 20 이렇게 개발자들이 정해주는 것이기 때문에 해당 부분을 개발한 개발자가 아니라면 이 숫자가 의미하는 Activity를 파악하기 어렵다는 문제점도 가지고 있습니다.

반면 새로운 API를 보겠습니다.

   private lateinit var getResultImage: ActivityResultLauncher<Intent>
   
    private fun loadImage() {

        var intent_image = Intent()
        intent_image.type = "image/*"
        intent_image.action = Intent.ACTION_GET_CONTENT

        getResultImage.launch(intent_image)
    }
   
   ....

   ....
   
    getResultImage = registerForActivityResult(
            ActivityResultContracts.StartActivityForResult()
        ) { result ->
            if (result.resultCode == RESULT_OK) {
                val dataUri: Uri? = result.data?.data
                try {
                    val bitmap: Bitmap =
                        MediaStore.Images.Media.getBitmap(context?.contentResolver, dataUri)
                    binding.profileImage.setImageBitmap(bitmap)
                } catch (e: Exception) {
                    Toast.makeText(context, "$e", Toast.LENGTH_SHORT).show()
                }
            }
        }

result.resulCode나 result.data등으로 결과 코드와 데이터에 접근이 가능해지며
launch()함수로 시작하게 됩니다. requestCode가 없어짐에 따라 resultCode만 확인하고 바로 작업이 가능하다라는 장점이 있습니다.

0개의 댓글