ActivityResultContractのコードを少し読んでみる

Activity 1.2.0-alpha02 と Fragment 1.3.0-alpha02で追加されたAPIのようで、型安全にstartActivityForResultを扱えるようです。またテストもできるようです。

コード例
AppCompatActivityやFragmentで`prepareCall()`でlauncherを用意して、`launch`を呼ぶと`startActivityForResult`を行うことができるようです。

```kotlin:MainActivity.kt
val intent = Intent(this, SecondActivity::class.java)
val launcher: ActivityResultLauncher<Intent> = prepareCall(
        ActivityResultContracts.StartActivityForResult()
) { activityResult: ActivityResult ->
    Log.d("MainActivity", activityResult.toString())
    //  D/MainActivity: ActivityResult{resultCode=RESULT_OK, data=Intent { (has extras) }}
}

fab.setOnClickListener { view ->
    launcher.launch(intent)
}
```

```kotlin:SecondActivity.kt
class SecondActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setResult(Activity.RESULT_OK, Intent().putExtra("my-data", "data"))
        finish()
    }
}
```

## コードを剥がしていく

ちょっと魔法みたいなので、中身を覗いて、今のコードに適応させてみましょう。

### ActivityResultContractを直接使ってみる

ActivityResultContracts.StartActivityForResultクラスは以下のように `ActivityResultContract`を継承しています。

```java:ActivityResultContracts.java
public class ActivityResultContracts {
    private ActivityResultContracts() {}
...
    public static class StartActivityForResult
            extends ActivityResultContract<Intent, ActivityResult> {

        @NonNull
        @Override
        public Intent createIntent(@NonNull Intent input) {
            return input;
        }

        @NonNull
        @Override
        public ActivityResult parseResult(int resultCode, @Nullable Intent intent) {
            return new ActivityResult(resultCode, intent);
        }
    }
```

そのため`ActivityResultContract`を使って書き直して以下と同じです。
ここでは型引数としてIntentとActivityResultクラスをとっています。このように**自分でstartActivityのときに使いたい引数の型とActivityResultで受け取る型も型引数で指定して書いていくことができます。**

```kotlin:MainActivity.kt
val launcher: ActivityResultLauncher<Intent> = prepareCall(
        // ** ↓ **
        object : ActivityResultContract<Intent, ActivityResult>() {
            override fun createIntent(input: Intent): Intent {
                return input
            }

            override fun parseResult(resultCode: Int, intent: Intent?): ActivityResult {
                return ActivityResult(resultCode, intent)
            }
        }
        // ** ↑ **
) { activityResult: ActivityResult ->
    Log.d("MainActivity", activityResult.toString())
    //  D/MainActivity: ActivityResult{resultCode=RESULT_OK, data=Intent { (has extras) }}
}
```

### ActivityResultRegistryを直接使ってみる

またComponentActivity.prepareCallは以下のように、mActivityResultRegistryにregisterActivityResultCallback()しています。

```java:ComponentActivity.java
    @NonNull
    @Override
    public <I, O> ActivityResultLauncher<I> prepareCall(
            @NonNull ActivityResultContract<I, O> contract,
            @NonNull ActivityResultCallback<O> callback) {
        return prepareCall(contract, mActivityResultRegistry, callback);
    }

    @NonNull
    @Override
    public <I, O> ActivityResultLauncher<I> prepareCall(
            @NonNull final ActivityResultContract<I, O> contract,
            @NonNull final ActivityResultRegistry registry,
            @NonNull final ActivityResultCallback<O> callback) {
        return registry.registerActivityResultCallback(
                "activity_rq#" + mNextLocalRequestCode.getAndIncrement(), this, contract, callback);
    }

    @NonNull
    public ActivityResultRegistry getActivityResultRegistry() {
        return mActivityResultRegistry;
    }
```

そのため`ComponentActivity.getActivityResultRegistry()`をして`ActivityResultRegistry.registerActivityResultCallback()`を利用すると以下のように書くことができます。

```kotlin:MainActivity.kt
        // ** ↓ **
val launcher: ActivityResultLauncher<Intent> = activityResultRegistry
        .registerActivityResultCallback(
                "activity_rq#0", // ←この数字は本来は呼び出すたびにincrementが必要
        // ** ↑ **
                object : ActivityResultContract<Intent, ActivityResult>() {
                    override fun createIntent(input: Intent): Intent {
                        return input
                    }

                    override fun parseResult(resultCode: Int, intent: Intent?): ActivityResult {
                        return ActivityResult(resultCode, intent)
                    }
                }
        ) { activityResult: ActivityResult ->
            Log.d("MainActivity", activityResult.toString())
            //  D/MainActivity: ActivityResult{resultCode=RESULT_OK, data=Intent { (has extras) }}
        }
```

**`registerActivityResultCallback(key, ActivityResultCallback, ActivityResultContract)`を使うと`ActivityResultRegistory`が持っているHashMapにputされます**

## ComponentActivity周りのかんたんな構造

単純にActivityがActivityResultRegistoryを持っていて、ActivityResultRegistoryがActivityResultContractとActivityResultCallbackを持っているようです。
このHashMapのキーは`registerActivityResultCallback`に渡される `"activity_rq#" + mNextLocalRequestCode.getAndIncrement()`などになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27388/f0ce47e6-4b1f-6cde-b121-c36acd5be918.png)

## 今まであったrequest codeはどうなったか？
今まではstartActivityForResultを呼び出すときに呼び出しと返却を識別するために使うrequest codeというのが必要でしたが、それが利用するときに必要なくなりました。こちらもAutoIncrementして管理しています。また、プロセスが死んでも再度使えるように`onSaveInstanceState`でrequest codeとActivityResultRegistoryのkeyのペアを管理して、保存してうまくやってくれるようです

```java:ActivityResultRegistry.java
    private int registerKey(String key) {
        Integer existing = mKeyToRc.get(key);
        if (existing != null) {
            return existing;
        }
        int rc = mNextRc.getAndIncrement();
        bindRcKey(rc, key);
        return rc;
    }
```

## Fragmentでメモリリークしないのか？

`Fragment.prepareCall()`の実装を見るとFragmentでON_CREATEになったときに`getActivity().getActivityResultRegistry().registerActivityResultCallback`します。Activityのスコープの変数にFragmentで作られたコールバックを突っ込むため、
これだけ見るとリークしてそうに見えます。

```java:Fragment.java
    public <I, O> ActivityResultLauncher<I> prepareCall(
            @NonNull final ActivityResultContract<I, O> contract,
            @NonNull final ActivityResultCallback<O> callback) {
...
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner lifecycleOwner,
                    @NonNull Lifecycle.Event event) {

                if (Lifecycle.Event.ON_CREATE.equals(event)) {
                    ref.set(getActivity()
                            .getActivityResultRegistry()
                  // **ここでregisterActivityResultCallback**
                            .registerActivityResultCallback(
                                    key, Fragment.this, contract, callback));
                }
            }
        });

        return new ActivityResultLauncher<I>() {
            @Override
            public void launch(I input) {
...
            }
        };
    }
```

しかし、以下でLifecycleOwnerとして渡しています。

```java:Fragment.java
registerActivityResultCallback(
                                    key, Fragment.this  // **←ここでLifecycleOwnerとして渡す**
```

また、以下のようにON_DESTROYでunregisterActivityResultCallbackするため問題なさそうに見えます。

```java:ActivityResultRegistry.java
       lifecycle.addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner lifecycleOwner,
                    @NonNull Lifecycle.Event event) {
                if (Lifecycle.Event.ON_DESTROY.equals(event)) {
                    unregisterActivityResultCallback(key);
                }
            }
        });
```

## まとめ
* ActivityResultContractに型引数を渡すことで、渡す方と受け取る型を指定していけそう。
* ComponentActivity.ActivityResultRegistryでコールバックなどは管理されている。
* この部分に関してFragmentのリークは気にしなくても良さそう。

## 参考
https://android-review.googlesource.com/c/platform/frameworks/support/+/1233518
https://developer.android.com/training/basics/intents/result
https://medium.com/@star_zero/activityresultcontract%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9Factivity%E9%96%93%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E5%8F%97%E3%81%91%E6%B8%A1%E3%81%97-17052bfafde0