android

registerForActivityResult 메서드의 동작과정

android by kotlin 2022. 2. 3. 20:09
//1. ActivityResultRegistry를 구현한 객체를 생성한다. 개발자가 ActivityResultLauncher.launch() 메서드를 호출할 때 사용될 onLaunch 메서드를 재정의해두었다.
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ContextAware,
        LifecycleOwner,
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner,
        ActivityResultRegistryOwner,
        ActivityResultCaller {

    //...
    private final ActivityResultRegistry mActivityResultRegistry = new ActivityResultRegistry() {

        @Override
        public <I, O> void onLaunch(
                final int requestCode,
                @NonNull ActivityResultContract<I, O> contract,
                I input,
                @Nullable ActivityOptionsCompat options) {

            ...

            else {
                // startActivityForResult path
                ActivityCompat.startActivityForResult(activity, intent, requestCode, optionsBundle);
            }
        }
}
//2. 콜백객체와 Contract 를 HashMap 에 저장하는 메서드를 호출한다.
@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);
}
//4. 생명주기를 확인해 STARTED 이면 예외를 발생시키고 종료되며
//    STARTED 이전이면 LifecycleEventObserver를 등록해 onStart 생명주기가 되었을 때 Contract와 콜백객체를 HashMap에 저장하며
//    ActivityResultLauncher 객체를 생성하여 반환해준다.

@NonNull
public final <I, O> ActivityResultLauncher<I> register(
        @NonNull final String key,
        @NonNull final LifecycleOwner lifecycleOwner,
        @NonNull final ActivityResultContract<I, O> contract,
        @NonNull final ActivityResultCallback<O> callback) {

    Lifecycle lifecycle = lifecycleOwner.getLifecycle();

    //생명주기가 STARTED 이후라면 예외를 발생시킨다.(registerForActivityResult 메서드는 onStart 이전 단계에서 호출되지 않으면 예외가 발생한다.)
    if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
        throw new IllegalStateException("LifecycleOwner " + lifecycleOwner + " is "
                + "attempting to register while current state is "
                + lifecycle.getCurrentState() + ". LifecycleOwners must call register before "
                + "they are STARTED.");
    }

    //...
    LifecycleEventObserver observer = new LifecycleEventObserver() {
        @Override
        public void onStateChanged(
                @NonNull LifecycleOwner lifecycleOwner,
                @NonNull Lifecycle.Event event) {
            if (Lifecycle.Event.ON_START.equals(event)) {

                //생명주기가 onStart 가 되었을 때 mKeyToCallback HashMap 에 콜백객체와 Contract 를 넣는다.
                mKeyToCallback.put(key, new ActivityResultRegistry.CallbackAndContract<>(callback, contract));
                if (mParsedPendingResults.containsKey(key)) {
                    @SuppressWarnings("unchecked") final O parsedPendingResult = (O) mParsedPendingResults.get(key);
                    mParsedPendingResults.remove(key);
                    callback.onActivityResult(parsedPendingResult);
                }
                final ActivityResult pendingResult = mPendingResults.getParcelable(key);
                if (pendingResult != null) {
                    mPendingResults.remove(key);
                    callback.onActivityResult(contract.parseResult(
                            pendingResult.getResultCode(),
                            pendingResult.getData()));
                }
            }
            //...
        }
    };

    //ActivityResultLauncher 객체를 반환해준다. 이 객체의 launch 메서드를 호출하여 Intent 를 실행시킬 수 있다.
    return new ActivityResultLauncher<I>() {
        @Override
        public void launch(I input, @Nullable ActivityOptionsCompat options) {
            mLaunchedKeys.add(key);
            Integer innerCode = mKeyToRc.get(key);
            onLaunch((innerCode != null) ? innerCode : requestCode, contract, input, options);
        }

        @Override
        public void unregister() {
            ActivityResultRegistry.this.unregister(key);
        }

        @NonNull
        @Override
        public ActivityResultContract<I, ?> getContract() {
            return contract;
        }
    };
}
//5. 반환받은 ActivityResultLauncher 의 launch 메서드를 호출해 Intent 를 시작한다. 
//   이때 1번에서 overriding 한 onLaunch 메서드가 실행되며 onLaunch 메서드 내에서 startActivityResult 메서드를 호출하여 액티비티 전환이 발생한다.
private fun signIn() {
    val signInIntent = googleSignInClient.signInIntent
    activityForResult.launch(signInIntent)
}
//6. 이전 액티비티로 반환되었을 때 호출된다.
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    if (!mActivityResultRegistry.dispatchResult(requestCode, resultCode, data)) {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

//7. 저장해두었던 콜백객체와 Contract 를 가져와서 콜백객체의 onActivityResult(registerForActivityResult 메서드를 호출할 때 전달한 람다식)이 호출된다.
private <O> void doDispatch(String key, int resultCode, @Nullable Intent data,
                            @Nullable ActivityResultRegistry.CallbackAndContract<O> callbackAndContract) {
    if (callbackAndContract != null && callbackAndContract.mCallback != null) {
        ActivityResultCallback<O> callback = callbackAndContract.mCallback;
        ActivityResultContract<?, O> contract = callbackAndContract.mContract;
        callback.onActivityResult(contract.parseResult(resultCode, data));
    } else {
        // Remove any parsed pending result
        mParsedPendingResults.remove(key);
        // And add these pending results in their place
        mPendingResults.putParcelable(key, new ActivityResult(resultCode, data));
    }
}