[시작 전 반성]
한동안 Jetpack navigation 을 사용해서 프로젝트를 진행해서 startForActivityForResult 가 Deprecated 인지 몰랐다.
(알게된 건 작년 여름쯤)
Deprecated 라는 것은 더이상 개발되지 않고 사라질 수 있으니 사용을 지양하라는 의미로 알고 있어서 대체제를 찾았다.
찾아보니 Activity 를 시작하고 값을 받아오는 기능 뿐만 아니라 Permission 을 받아오는 기능을 깔끔한 콜백코드로 대체할 수 있어서 좋았다.
프로젝트에 치여 기록을 잊지말자는 반성으로 쓰기 시작해야겠다.
이 글은 위 문서를 읽을 때 이해하기 쉽게하기 위한 전반적인 내용과
내가 실제 프로젝트에 사용한 예시코드를 담고있다.
[Activity Result APIs]
Activity Result APIs 는 result 가 시스템에의해 전달되었을 때 result 를 registering 하고 launching 하고 handling 할 수 있게 해준다.
[왜 생겨났을 지 예상해보자]
액티비티를 새로 하나 시작했을 때 카메라같은 메모리를 많이 먹는 작업을 할 때 메모리가 부족하다면 앱의 프로세스와 액티비티가 시스템에 의해 죽을 수 있다.
그래서 result 콜백과 새 액티비티를 launch 하는 코드를 분리한다.
즉, 액티비티가 다시 만들어질 때 콜백을 사용하기 위해, result 콜백이 액티비티가 만들어질 때마다 등록되게 한다.
추가적으로 내 생각엔 기존 onActivityResult 와 onRequestPermissionsResult 같은 것은 오버라이딩하여 리퀘스트 코드를 관리하고 클래스 중간쯤에 함수를 추가로 작성하는 게 가독성을 떨어뜨린다고 생각한다.
그래서 나는 Activity Result APIs 가 굉장히 편하다고 느껴졌다.
[사용 개념]
ComponentActivity 나 Fragment 에서 Activity Result APIs 는 result 콜백을 등록하기 위해 registerForActivityResult 을 제공한다.
registerForActivityResult
registerForActivityResult 은 ActivityResultContract 와 ActivityResultCallback 을 받고 ActivityResultLauncher 를 반환한다. (이 함수를 사용한다고 바로 액티비티를 시작하고 요청을 하는 게 아니다. 그저 ActivityResultLauncher 객체 반환)
이 ActivityResultLauncher 객체로 다른 액티비티를 launch 해서 액티비티를 시작하고 요청하는 것이다.
ComponentActivity 와 Fragment 는 ActivityResultCaller 라는 인터페이스 구현체이기 때문에 registerForActivityResult 를 사용할 수 있다.
만약 ActivityResultCaller 구현체가 아닌 클래스에서 activity result 를 받고 싶다면 ActivityResultRegistry 를 직접 사용하면 된다.
ActivityResultContract
ActivityResultContract 는 result 를 생성하는데 필요한 input type 과 result 의 output type 을 정의한다.
여기서 input type 은 launch 함수의 인자로 전달해야하는 타입이고 output type 는 callback 으로 전달받는 인자이다.
그리고 default contract 를 제공한다. 사진을 찍고 permission 요청 하는 것 같은 기본적인 intent action 을 default contract 로 지원한다.
물론 custom contract 도 만들 수 있다. (이에 대해선 상단 링크의 아랫부분을 참고하면 된다.)
ActivityResultCallback
ActivityResultCallback 은 onActvityResult 라는 메소드를 하나 가진 인터베이스다. (그래서 람다로 뺄 수 있다.) 그 메소드는 ActivityResultContract 에 정의된 output type 객체를 인자로 받는다.
요청한 작업이 끝났을 때 이 콜백이 수행된다.
launch
result를 생성하는 프로세스를 시작한다.
ActivityResultContract 의 input type 으로 launch 의 인자로 전달한다.
val getContent = registerForActivityResult(GetContent()) { uri: Uri? ->
// ActivityResultContract 에 정의된 output type 객체를 여기서 처리하면 된다.
}
override fun onCreate(savedInstanceState: Bundle?) {
// ...
val selectButton = findViewById<Button>(R.id.select_button)
selectButton.setOnClickListener {
// ActivityResultContract 의 input type
getContent.launch("image/*")
}
}
[예시 코드]
Permission 받아오기
val startForRequestAccessFineLocationPermission =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
if (it[Manifest.permission.ACCESS_FINE_LOCATION] != true || it[Manifest.permission.ACCESS_COARSE_LOCATION] != true) {
showToast(R.string.toast_must_permit_access_fine_location)
} else {
startScanBluetoothDevicesByBLE()
}
}
private fun requestAccessFineLocationPermission() {
startForRequestAccessFineLocationPermission
.launch(
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
}
갤러리
private val galleryCallback =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
uri?.let {
uploadImage(uri, 1)
}
}
galleryCallback.launch("*/*")
액티비티 시작
private val startActivityForResultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
activityResult.data?.let { intent ->
// TODO 가져온 결과 처리
}
}
private fun startCoinDetailActivity(coinId: String) {
val intent = Intent(this@CoinListActivity, CoinDetailActivity::class.java)
intent.putExtra(PARAM_COIN_ID, coinId)
startActivityForResultLauncher.launch(intent)
}
// DetailActivity .. 특정버튼을 누르면 아래 코드가 실행된다고 가정해보자
Intent().putExtra(PARAM_COIN_ID, binding.tvCoinDetail.text.toString()).also { i ->
setResult(RESULT_OK, i)
finish()
}
'Android > 실전 회고' 카테고리의 다른 글
[Android/Kotlin] 딱 정리해, targetSdk 33 후 알림 권한요청 (Feat. 푸시 옵트인 작업) (0) | 2022.12.25 |
---|---|
[Android/Kotlin] Jetpack Compose, Navigation component, BottomNavigation 사용하여 화면이동 세팅 (1) | 2022.12.09 |
[Android] Firebase 기반 채팅 구현 적용기와 앞으로의 계획 (MVVM) (1) | 2022.02.26 |
[Android] 도메인(패키지명) 변경에 주의할 점 (0) | 2021.08.31 |