startActivityForResult()가 deprecated된 이유
- AndroidX Activity, Fragment에 도입된 Activity Result API 사용을 적극 권장하고 있음
- 결과를 얻는 Activity를 실행하는 로직을 사용할 때, 메모리 부족으로 인해 프로세스와 Activity가 사라질 수 있음
기존 Activity에서는 startActivityResult를 통해서 콜백을 등록하고, onActivityResult에서 콜백을 처리한다.
두 메서드가 같은 곳에서 구현을 해야하는데, 메모리 부족으로 제대로 동작을 안할 수 있다는 것
- Activity가 종료되었다가 다시 생성되었을 때 Activity에게 결과를 기다리는 중임을 다시 알려야 한다.
기존 API
화면 전환
- startActivity(Intent intent) : 새로운 Activity 시작 (단방향)
- startActivityForResult(Intent intent, int requestCode, Bundle options) : 새로운 Activity 시작 + 결괏값 전달 (쌍방향)
startActivityForeResult는 startActivity와 다르게 requestCode를 이용하여 어떤 Activity인지 식별할 수 있다.
결과 반환 및 전달
- setResult(int resultCode)
- Activity가 종료되면 해당 메서드를 통하여 데이터를 상위 항목으로 되돌릴 수 있음
- 새롭게 시작한 Activity의 종료시점에 호출하여 데이터를 상위(parent)로 보내고 resultCode를 전달해야 함
- resultCode : RESULT_OK, RESULT_CANCELED(충돌 등으로 실패하는 경우), 사용자 지정
- 이 모든 정보는 상위(parent)의 Activity.onActivityResult()에 다시 나타남
- onActivityResult(int requestCode, int resultCode, Intent data) : 실행한 Activity가 종료되어 시작한 requestCode, 반환된 resultCode, 추가 데이터를 제공할 때 호출
새로운 API
Activity Result API는 다른 Activity를 실행하는 코드에서 결과 콜백을 분리한다.
Result Callback은 프로세스와 Activity를 다시 생성할 때 사용할 수 있어야 하므로 다른 Activity를 실행하는 로직이 사용자 입력 또는 기타 비즈니스 로직을 기반으로 발생하더라도 Activity가 생성될 때마다 콜백을 무조건 등록해야 한다.
registerForActivityResult()는 ActivityResultContract 및 ActivityResultCallback을 가져와서 다른 Activity를 실행하는데 사용할 ActivityResultLauncher를 반환함
registerForActivityResult() 메서드는 함수명에서 알 수 있듯이, 콜백을 등록하는 역할을 한다.
따라서 ActivityResultLauncher와 registerForActivityResult() 메서드를 사용하면 Activity가 종료되었다가 다시 생성되어도 결과를 기다리고 있다는 것을 알려줄 수 있는 것이다.
새로운 API는 requestCode가 없음
원하는 Activity Request마다 registerForActivityResult를 실행하여 Result Callback을 분리해서 구현하므로
Activity를 구분할 필요가 없어졌기 때문
private lateinit var getResultLauncher: ActivityResultLauncher<Intent>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
getResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
//처리 로직
}
}
...
binding.signInGoogle.setOnclickListener {
val signInIntent = googleSignInClient.signInIntent
getResultLauncher.launch(signInIntent)
}
}
- 람다식의 it은 결과를 나타낸다. (result 등으로 인자를 명시하고 사용해도 무관)
- it.resultCode, it.data 등으로 결과 코드(resultCode)와 데이터(Intent)에 접근할 수 있음
- launch() 함수로 시작함
함수의 인자로 들어가는 함수는 ActivityResultContracts 클래스의 static 함수로, 결과를 받기 위해 Activity를 실행하는 StartActivityForResult() 함수를 인자로 넣는다.
launch()를 통해 이동한 새로운 Activity에서는 기존과 동일하게 setResult() 함수를 그대로 사용하면 된다
→ resultCode와 Intent 데이터를 인자로 가지는 함수
Fragment에서 사용시 유의사항
아래와 같이 Activity에서 사용하던것 처럼 사용을 할 시 오류가 난다.
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding: FragmentHomeBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false)
private val startForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
var imageUrl = it.data?.data
...
}
}
binding.userImage.setOnClickListener {
var photoPickerIntent = Intent(Intent.ACTION_PICK)
photoPickerIntent.type = "image/*"
startForResult.launch(photoPickerIntent)
}
return binding.root
}
Fragments must call registerForActivityResult() before they are created (i.e. initialization, onAttach(), or onCreate()).
Fragment는 생성되기 전(즉, initialization, onAttach(), onCreate())에 registerForActivityResult()를 호출해야 한다.)라는 오류 내용을 알려준다.
공식문서
결과를 얻기 위해 Activity를 시작할 때, 메모리 부족으로 인해 프로세스와 Activity가 소멸될 수 있다.
따라서 Activity Result API는 다른 Activity를 실행하는 코드에서 결과 콜백을 분리한다.
프로세스 및 작업을 다시 생성할 때 결과 콜백을 사용할 수 있어야 하므로 다른 작업을 시작하는 로직이 사용자 입력 또는 다른 비즈니스 로직에 따라서만 발생하더라도 작업이 생성될 때마다 콜백을 무조건 등록해야 한다.
공식문서에 따라 콜백을 다시 등록할 수 있게 initialization, onAttach(), onCreate())에 registerForActivityResult()를 호출한다.
아래는 initialization으로 미리 선언 후 사용한 예
class HomeFragment : Fragment(){
private val startForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
...
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding: FragmentHomeBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false)
binding.userImage.setOnClickListener {
var photoPickerIntent = Intent(Intent.ACTION_PICK)
photoPickerIntent.type = "image/*"
startForResult.launch(photoPickerIntent)
}
return binding.root
}
}
Result API
https://junyoung-developer.tistory.com/151
https://junyoung-developer.tistory.com/159
'Android > Debugging' 카테고리의 다른 글
Android GoogleMap Ensure that the "Google Maps Android API v2" is enabled. 구글맵 키 오류 (0) | 2022.08.01 |
---|---|
무선 Debugging 사용 (0) | 2022.03.22 |
Exception 종류 (0) | 2022.03.10 |
Emulator Toast 보이지 않을때 (0) | 2022.03.09 |
Emulator / System UI isn't responding 해결법 (0) | 2022.01.19 |