관리 메뉴

막내의 막무가내 프로그래밍 & 일상

[안드로이드] startActivityForResult() onActivityResult() deprecated 해결 방법 !! (갤러리에서 사진불러오기로 예시 2021) 본문

안드로이드/코틀린 & 아키텍처 & Recent

[안드로이드] startActivityForResult() onActivityResult() deprecated 해결 방법 !! (갤러리에서 사진불러오기로 예시 2021)

막무가내막내 2021. 3. 23. 22:23
728x90

 

 

 

 

 

최근 안드로이드 스튜디오도 업데이트하고 새로운 프로젝트를 하나 생성해서 개발을 하고 있습니다.

 

 

갤러리 사진 요청 인텐트
갤러리 사진 pick 응답

그러던 중 갤러리에서 사진을 불러오기 위해 평소대로 위와 같이 startActivityForResult(), onActivityResult()  를 사용하였는데 deprecated 되었고 registerForActivityResult() 을 사용하라는 문구를 봤습니다.

 

안드로이드 처음할 때 갤러리, 카메라 사진 불러올때나 액티비티간 이동시 startActivityForResult()를 통해 값을 주고받았는데 이것도 업데이트가 되었다니 기본적인 함수라 변경이 안될줄알았는데 정말 안드로이드는 변화가 빠른것같습니다. (2020년 5월쯤 된 것 같은데 지금 알다니 ... ㅠ )

 

그래서 우리는 이제 구글에서 권장한 대로 Activity 클래스의 기본 startActivityForResult() onActivityResult() API는 모든 API 수준에서 사용할 수 있지만, AndroidX Activity 1.2.0-alpha02 Fragment 1.3.0-alpha02에 도입된 Activity Result API를 사용하는 것이 더 좋습니다.

 

[필독]

developer.android.com/training/basics/intents/result

 

활동에서 결과 가져오기  |  Android 개발자  |  Android Developers

개발자 앱 내의 활동이든 다른 앱의 활동이든 다른 활동을 시작하는 것이 단방향 작업일 필요는 없습니다. 다른 활동을 시작하고 다시 결과를 받을 수도 있습니다. 예를 들어, 앱에서 카메라 앱

developer.android.com

먼저 이에 대한 자세한 내용과 다양한 구현 방법은 위 공식문서에 아주 친절하게 나와있으니 참고하시면될 것 같습니다. 저도 공부할때 공식문서를 참고하면서 할 예정이므로 이 포스팅에서는 예제만 하나 남겨놓고 이후에 다른 케이스를 사용하게되는 경우 추가 내용을 남기도록 하겠습니다 :) 공식문서만 믿고 따라가십쇼ㅎㅎ

 

 

 

먼저 기본적인 구현 방법은 기존의  startActivityForResult(), onActivityResult() 처럼 크게 2가지로 나뉩니다. 

 

 

 

 

들어가기에 앞서 저는 액티비티가 아니라 프래그먼트 환경에서 했다는 점 참고해주시기 바랍니다 :)

1. Activity Result에 콜백 등록 (Registering a callback for an Activity Result)

[개념]

결과를 얻기 위해 활동을 시작할 때, 메모리 부족으로 인해 프로세스와 활동이 소멸될 수 있습니다(특히, 카메라 사용과 같이 메모리를 많이 사용하는 작업의 경우 소멸 확률이 매우 높음).

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

 

ActivityResultContract는 결과를 생성하는 데 필요한 입력 유형과 결과의 출력 유형을 정의합니다. 이 API는 사진 촬영, 권한 요청 등과 같은 기본 인텐트 작업의 기본 계약을 제공합니다. 자체 맞춤 계약을 만들 수도 있습니다.

ActivityResultCallback은 ActivityResultContract에 정의된 출력 유형의 객체를 가져오는 onActivityResult() 메서드가 포함된 단일 메서드 인터페이스입니다.

 

 

[구현 내용]

 

저는 사진을 갤러리에서 불러오는 것을 구현해야했는데 커스텀 Contracts(자체 맞춤 계약)을 사용할 필요가 없어서 정의된 기본 Contracts(계약)을 사용했습니다. 위와 같이 기본적인 인텐트들은 다 제공한다고 보시면 됩니다. (ex. 사진찍기, 불러오기, 전화번호부불러오기, 문서, 권한요청 등등)

private val getContent =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
            binding.ivProfileImage.setImageURI(result.data?.data)
        }

 

 

2. 결과를 위한 활동 실행 (Launching an activity for result)

[개념]

registerForActivityResult()는 콜백을 등록하지만, 다른 활동을 실행하거나 결과 요청을 시작하지 않습니다. 대신, 이 작업은 반환된 ActivityResultLauncher 인스턴스가 담당합니다.

입력이 있으면 런처는 ActivityResultContract 유형과 일치하는 입력을 가져옵니다. launch()를 호출하면 결과를 생성하는 프로세스가 시작됩니다. 사용자가 후속 활동을 완료하고 반환하면 ActivityResultCallback의 onActivityResult()가 다음 예와 같이 실행됩니다.

 

 

[구현 내용]

갤러리에서 사진을 한 장 선택해서 불러올 수 있게 인텐트 타입을 설정해줬습니다.

                val intent = Intent(Intent.ACTION_PICK)
                intent.type = MediaStore.Images.Media.CONTENT_TYPE
                intent.type = "image/*"
                getContent.launch(intent)

 

 

 

PLUS. 갤러리에서  사진 불러올때 삽질

처음에 저는 위와 같이 구글공식문서에서 제공하는 방법으로(https://developer.android.com/training/basics/intents/result#launch) 갤러리에서 사진을 불러오려고 했습니다. 기본으로 제공해주는 Contrat 함수인 GetContent()를 써서말이죠.

하지만 이 방법은 밑 그림과 같이 최근 이미지 몇장만 볼 수 있게 UI IMAGE PICKER 가 뜨게 됩니다. 

그리고 갤러리 아이콘을 누르면 갤러리로 가지기는 하는데 사진을 선택해도 선택이 안되게 됩니다. 

 

최근 이미지 밑에는 최근 사진만 몇장 나열되어있는데 사적이라 생략했습니다. 

 

 

그래서 위에서 설명한 ActivityResultContracts.StartActivityForResult() 계약(Contratct)을 사용하여 해결했습니다.

해결

 

 

 

 

 

 

 

전체코드는 다음과 같습니다.

package com.mtjin.lolrankduo.views.profile

import android.content.Intent
import android.provider.MediaStore
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.lifecycle.Observer
import com.mtjin.lolrankduo.R
import com.mtjin.lolrankduo.base.BaseFragment
import com.mtjin.lolrankduo.databinding.FragmentProfileBinding
import org.koin.androidx.viewmodel.ext.android.viewModel


class ProfileFragment : BaseFragment<FragmentProfileBinding>(R.layout.fragment_profile) {

    private val viewModel: ProfileViewModel by viewModel()
    private val getContent =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
            binding.ivProfileImage.setImageURI(result.data?.data)
        }

    override fun init() {
        binding.vm = viewModel
        binding.toolbar.profileVm = viewModel
        initViewModelCallback()
    }

    private fun initViewModelCallback() {
        with(viewModel) {
            pickImage.observe(this@ProfileFragment, Observer {
                val intent = Intent(Intent.ACTION_PICK)
                intent.type = MediaStore.Images.Media.CONTENT_TYPE
                intent.type = "image/*"
                getContent.launch(intent)
            })
        }
    }
}

 

 

 

 

 

 

이밖에도 별도의 클래스에서 이러한 활동결과들을 수신하는 것을 분리시킬수 있는 다양한 방법들이 공식문서에 적혀있으므로 확인하시면 좋을 것 같습니다.

 

댓글과 공감은 큰 힘이 됩니다. 감사합니다. !!

 

 

 

 

728x90
Comments