이전 포스팅에서 activity intent를 정리하다보니 result 수신에 사용하는onActivityResult()
가 deprecated 되었네요. 대신에 AndroidX Activity와 Fragment에 도입된 Activity Result API 사용을 적극 권장되고 있습니다.
먼저 result callback을 받기 위해서는 process 및 activity를 재 생성하게 될때 생성할때마다 callback을 실행해야 합니다. 이것은 registerForAcitivityResult()
를 사용하여 받을 수 있습니다.
@NonNull
@Override
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final ActivityResultRegistry registry,
@NonNull final ActivityResultCallback<O> callback) {
return registry.register(
"activity_rq#" + mNextLocalRequestCode.getAndIncrement()
, this, contract, callback);
}
@NonNull
@Override
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull ActivityResultContract<I, O> contract,
@NonNull ActivityResultCallback<O> callback) {
return registerForActivityResult(contract
, mActivityResultRegistry, callback);
}
액티비티의 intent를 파싱하거나 생성하는데 사용하고 있습니다.
abstract class ActivityResultContract<I, O> {
// Activity#startActivityForResult
// intent 생성
abstract fun createIntent(context: Context, input: I): Intent
// Activity#onActivityResult
// intent 파싱
abstract fun parseResult(resultCode: Int, intent: Intent?): O
// 결과를 빌드할 필요 없이 주어진 입력의 결과를 확인할 수 있는 경우
open fun getSynchronousResult(context: Context, input: I)
: SynchronousResult<O>? {
return null
}
class SynchronousResult<T>(val value: T)
}
ActivityResultContract
를 상속받아 custom contract를 작성하게 할 수 있습니다.
ActivityResultContract#createIntent
는 데이터를 전달할때 사용하는 intent를 작성할 수 있고 ActivityResultContract#parseResult
는 데이터를 받아 파싱하여 result intent를 가공할 수 있습니다
class PickRingtone : ActivityResultContract<Int, Uri?>() {
override fun createIntent(context: Context, ringtoneType: Int) =
Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {
putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, ringtoneType)
}
override fun parseResult(resultCode: Int, result: Intent?) : Uri? {
if (resultCode != Activity.RESULT_OK) {
return null
}
return result?
.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
}
}
custom contract를 작성하지 않고 기본으로 내장되어있는 contracts 가 존재하는데요 ActivityResultContracts
클래스를 참고 하면 정말 많은 양의 내장 contacts가 있습니다. 링크를 참조하세요
val getContent = registerForActivityResult(GetContent()) { uri: Uri? ->
// Handle the returned Uri
}
override fun onCreate(savedInstanceState: Bundle?) {
// ...
val selectButton = findViewById<Button>(R.id.select_button)
selectButton.setOnClickListener {
// Pass in the mime type you'd like to allow the user to select
// as the input
getContent.launch("image/*")
}
}
위 코드는 기본 내장된 이미지 갤러리를 열수 있는 버튼을 가진 액티비티 입니다.
ActivityResultLauncher#launch
를 사용하면 contact의 내용을 가지고 액티비티를 실행하거나 result값을 받을 수 있습니다.
package androidx.activity.result;
public abstract class ActivityResultLauncher<I> {
public void launch(@SuppressLint("UnknownNullness") I input) {
launch(input, null);
}
public abstract void launch(@SuppressLint("UnknownNullness") I input,
@Nullable ActivityOptionsCompat options);
@MainThread
public abstract void unregister();
@NonNull
public abstract ActivityResultContract<I, ?> getContract();
}
launch로 activity를 실행하면 ActivityResultRegistry에 등록이 되는 로직이 작동합니다
ActivityResultRegistry#register
의 내부에서 lifecycle의 onstatechaged()
가 발생될 때마다 실행되어 콜백을 받아서 리턴 처리할 수 있습니다.
.....
LifecycleEventObserver observer = new LifecycleEventObserver() {
public void onStateChanged(@NonNull LifecycleOwner lifecycleOwner, @NonNull Lifecycle.Event event) {
if (Event.ON_START.equals(event)) {
ActivityResultRegistry.this.mKeyToCallback.put(key, new CallbackAndContract(callback, contract));
if (ActivityResultRegistry.this.mParsedPendingResults.containsKey(key)) {
O parsedPendingResult = ActivityResultRegistry.this.mParsedPendingResults.get(key);
ActivityResultRegistry.this.mParsedPendingResults.remove(key);
callback.onActivityResult(parsedPendingResult);
}
ActivityResult pendingResult = (ActivityResult)ActivityResultRegistry.this.mPendingResults.getParcelable(key);
if (pendingResult != null) {
ActivityResultRegistry.this.mPendingResults.remove(key);
callback.onActivityResult(contract.parseResult(pendingResult.getResultCode(), pendingResult.getData()));
}
} else if (Event.ON_STOP.equals(event)) {
ActivityResultRegistry.this.mKeyToCallback.remove(key);
} else if (Event.ON_DESTROY.equals(event)) {
ActivityResultRegistry.this.unregister(key);
}
}
};
...
Event.ON_START
의 경우 callback가 있는지 확인하여 onActivityResult로 결과를 등록하며
Event.ON_STOP
의 경우에는 callback할 곳에서 현재 key를 삭제하여 레지스트리를 비우고 있습니다
Event.ON_DESTROY
의 경우에는 셋팅되어 있는 key에 해당하는 모든 결과들을 삭제하는 로직을 가지고 있습니다.
activity lifecycle을 다음에 정리해서 같이 보면 더좋을 것같습니다