Google は、黒人コミュニティに対する人種平等の促進に取り組んでいます。取り組みを見る

アクティビティからの結果の取得

別のアクティビティを開始する場合、アプリ内のアクティビティか別のアプリのアクティビティかに関係なく、一方向の操作である必要はありません。別のアクティビティを開始して、結果を受け取ることもできます。たとえば、アプリからカメラアプリを起動し、結果として撮影した写真を受け取ることができます。また、ユーザーが連絡先を選択するために連絡帳アプリを起動し、結果として連絡先の詳細を受け取ることもできます。

基盤となる startActivityForResult() API と onActivityResult() API はあらゆる API レベルの Activity クラスで使用できますが、AndroidX Activity 1.2.0-alpha02 および Fragment 1.3.0-alpha02 で導入された Activity Result API を使用することを強くおすすめします。

Activity Result API には、システムから結果がディスパッチされた後の結果の登録、結果に対するアクティビティの起動、結果の処理を行うためのコンポーネントが用意されています。

アクティビティの結果を取得する準備をする

アクティビティを開始して結果を取得する場合、メモリ不足が原因でプロセスやアクティビティが破棄される可能性があります(カメラの使用など、メモリを大量に消費する処理の場合は、ほぼ確実に破棄されます)。

そのため、Activity Result API では、他のアクティビティを開始するコードの場所から結果のコールバックを切り離しています。プロセスやアクティビティが再作成されたときに結果のコールバックを使用できる必要があるため、他のアクティビティを開始するロジックがユーザー入力やその他のビジネス ロジックに基づいてのみ実行される場合でも、アクティビティが作成されるたびにコールバックを無条件で登録する必要があります。

ComponentActivity または Fragment の Activity Result API には、結果のコールバックを登録するための prepareCall() が用意されています。prepareCall()ActivityResultContractActivityResultCallback を受け取って、他のアクティビティを開始するために使用する ActivityResultLauncher を返します。

ActivityResultContract では、結果の生成に必要な入力タイプと結果の出力タイプを定義します。API には、写真の撮影や権限のリクエストなどの基本的なインテント アクション用のデフォルトのコントラクトが用意されています。また、独自のカスタム コントラクトを作成することもできます。

ActivityResultCallback は、ActivityResultContract で定義した出力タイプのオブジェクトを受け取る onActivityResult() メソッドを含むシングル メソッド インターフェースです。

Kotlin

    val getContent = prepareCall(GetContent()) { uri: Uri? ->
        // Handle the returned Uri
    }
    

Java

    // GetContent creates an ActivityResultLauncher<String> to allow you to pass
    // in the mime type you'd like to allow the user to select
    ActivityResultLauncher<String> mGetContent = prepareCall(new GetContent(),
        new ActivityResultCallback<Uri>() {
            @Override
            public void onActivityResult(Uri uri) {
                // Handle the returned Uri
            }
    });
    

アクティビティの結果を複数回呼び出す際に、それぞれ別々のコントラクトを使用するか、個別のコールバックが必要な場合、prepareCall() を複数回呼び出して、複数の ActivityResultLauncher インスタンスを作成することができます。配信中の結果が適切なコールバックに提供されるようにするには、フラグメントまたはアクティビティが作成されるたびに prepareCall() を同じ順序で呼び出す必要があります。

prepareCall() は、フラグメントまたはアクティビティが作成される前に安全に呼び出すことができ、返される ActivityResultLauncher インスタンスのメンバー変数を宣言する際に直接使用できます。

結果に対するアクティビティを起動する

prepareCall() はコールバックの登録は行いますが、他のアクティビティは起動せず、結果に対するリクエストも開始しません。これらは、返された ActivityResultLauncher インスタンスが行います。

入力が存在する場合、ランチャーは ActivityResultContract のタイプと一致する入力を受け取ります。launch() を呼び出すと、結果の生成プロセスが開始されます。ユーザーがその後のアクティビティを完了して復帰すると、次の例に示すように、ActivityResultCallbackonActivityResult() が実行されます。

Kotlin

    val getContent = prepareCall(GetContent()) { uri: Uri? ->
        // Handle the returned Uri
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        // ...

        val selectButton = findViewById<Button>(R.id.select_button)

        selectButton.setOnClickListener {
            // Use the Kotlin extension in activity-ktx
            // passing it the mime type you'd like to allow the user to select
            getContent("image/*")
        }
    }
    

Java

    ActivityResultLauncher<String> mGetContent = prepareCall(new GetContent(),
        new ActivityResultCallback<Uri>() {
            @Override
            public void onActivityResult(Uri uri) {
                // Handle the returned Uri
            }
    });

    @Override
    public void onCreate(@Nullable savedInstanceState: Bundle) {
        // ...

        Button selectButton = findViewById(R.id.select_button);

        selectButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                // Pass in the mime type you'd like to allow the user to select
                // as the input
                mGetContent.launch("image/*");
            }
        });
    }
    

launch() のオーバーロードされたバージョンを使用すると、入力に加えて ActivityOptionsCompat も渡すことができます。

アクティビティの結果を別のクラスで受け取る

ComponentActivity クラスと Fragment クラスに実装されている ActivityResultCaller インターフェースで prepareCall() API を使用することもできますが、ActivityResultCaller を実装していない別のクラスで ActivityResultRegistry を直接使用してアクティビティの結果を受け取ることもできます。

たとえば、コントラクトの登録とランチャーの起動を処理する LifecycleObserver を実装することができます。

Kotlin

    class MyLifecycleObserver(private val registry : ActivityResultRegistry)
            : DefaultLifecycleObserver {
        lateinit var getContent : ActivityResultLauncher<String>

        fun onCreate(owner: LifecycleOwner) {
            getContent = registry.register("key", owner, GetContent()) { uri ->
                // Handle the returned Uri
            }
        }

        fun selectImage() {
            getContent("image/*")
        }
    }

    class MyFragment : Fragment() {
        lateinit var observer : MyLifecycleObserver

        override fun onCreate(savedInstanceState: Bundle?) {
            // ...

            observer = MyLifecycleObserver(requireActivity().activityResultRegistry)
            lifecycle.addObserver(observer)
        }

        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            val selectButton = view.findViewById<Button>(R.id.select_button)

            selectButton.setOnClickListener {
                // Open the activity to select an image
                observer.selectImage()
            }
        }
    }
    

Java

    class MyLifecycleObserver extends DefaultLifecycleObserver {
        private final ActivityResultRegistry mRegistry;
        private ActivityResultLauncher<String> mGetContent;

        MyLifecycleObserver(@NonNull ActivityResultRegistry registry) {
            mRegistry = registry;
        }

        public void onCreate(@NonNull LifecycleOwner owner) {
            // ...

            mGetContent = mRegistry.register(“key”, owner, new GetContent(),
                new ActivityResultCallback<String>() {
                    @Override
                    public void onActivityResult(Uri uri) {
                        // Handle the returned Uri
                    }
                });
        }

        public void selectImage() {
            // Open the activity to select an image
            mGetContent.launch("image/*");
        }
    }

    class MyFragment extends Fragment {
        private MyLifecycleObserver mObserver;

        @Override
        void onCreate(Bundle savedInstanceState) {
            // ...

            mObserver = MyLifecycleObserver(requireActivity().getActivityResultRegistry());
            getLifecycle().addObserver(mObserver);
        }

        @Override
        void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            Button selectButton = findViewById(R.id.select_button);
            selectButton.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View view) {
                    mObserver.selectImage();
                }
            });
        }
    }
    

ActivityResultRegistry API を使用する場合は、LifecycleOwner を受け取る API を使用することを強くおすすめします。LifecycleOwner は、Lifecycle が破棄されると、登録済みのランチャーを自動的に削除します。ただし、LifecycleOwner を使用できない場合は、代わりに各 ActivityResultLauncher クラスを使用して unregister() を手動で呼び出すことができます。

テスト

prepareCall() はデフォルトで、アクティビティから提供される ActivityResultRegistry を自動的に使用します。また、独自の ActivityResultRegistry インスタンスを渡すことができるオーバーロードを提供します。このインスタンスを使用すると、別のアクティビティを実際に起動せずにアクティビティの結果の呼び出しをテストできます。

アプリのフラグメントをテストする際にテスト用の ActivityResultRegistry を提供するには、FragmentFactory を使用してフラグメントのコンストラクタに ActivityResultRegistry を渡します。

たとえば、TakePicturePreview コントラクトを使用して画像のサムネイルを取得するフラグメントは、次のように記述できます。

Kotlin

    class MyFragment(
        private val registry: ActivityResultRegistry
    ) : Fragment() {
        val thumbnailLiveData = MutableLiveData<Bitmap?>

        val takePicture = prepareCall(TakePicturePreview(), registry) {
            bitmap: Bitmap? -> thumbnailLiveData.setValue(bitmap)
        }

        // ...
    }
    

Java

    public class MyFragment extends Fragment {
        private final ActivityResultRegistry mRegistry;
        private final MutableLiveData<Bitmap> mThumbnailLiveData = new MutableLiveData();
        private final ActivityResultLauncher<Void> mTakePicture =
            prepareCall(new TakePicturePreview(), mRegistry, new ActivityResultCallback<Bitmap>() {
                @Override
                public void onActivityResult(Bitmap thumbnail) {
                    mThumbnailLiveData.setValue(thumbnail);
                }
            });

        public MyFragment(@NonNull ActivityResultRegistry registry) {
            super();
            mRegistry = registry;
        }

        @VisibleForTesting
        @NonNull
        ActivityResultLauncher<Void> getTakePicture() {
            return mTakePicture;
        }

        @VisibleForTesting
        @NonNull
        LiveData<Bitmap> getThumbnailLiveData() {
            return mThumbnailLiveData;
        }

        // ...
    }
    

テスト固有の ActivityResultRegistry を作成する場合は、invoke() メソッドを実装する必要があります。startActivityForResult() を呼び出す代わりに、テストの実装で dispatchResult() を直接呼び出して、テストで使用する正確な結果を提供することもできます。

val testRegistry = object : ActivityResultRegistry() {
        override fun <I, O> invoke(
                requestCode: Int,
                contract: ActivityResultContract<I, O>,
                input: I,
                options: ActivityOptionsCompat?
        ) {
            dispatchResult(requestCode, expectedResult)
        }
    }
    

完全なテストでは、期待される結果を作成し、テスト用の ActivityResultRegistry を作成してフラグメントに渡し、ランチャーをトリガーします(直接トリガーするか、Espresso などの他のテスト用 API を介してトリガーします)。その後で結果を検証します。

@Test
    fun activityResultTest {
        // Create an expected result Bitmap
        val expectedResult = Bitmap.createBitmap(1, 1, Bitmap.Config.RGBA_F16)

        // Create the test ActivityResultRegistry
        val testRegistry = object : ActivityResultRegistry() {
                override fun <I, O> invoke(
                requestCode: Int,
                contract: ActivityResultContract<I, O>,
                input: I,
                options: ActivityOptionsCompat?
            ) {
                dispatchResult(requestCode, expectedResult)
            }
        }

        // Use the launchFragmentInContainer method that takes a
        // lambda to construct the Fragment with the testRegistry
        with(launchFragmentInContainer { MyFragment(testRegistry) }) {
                onFragment { fragment ->
                    // Trigger the ActivityResultLauncher
                    fragment.takePicture()
                    // Verify the result is set
                    assertThat(fragment.thumbnailLiveData.value)
                            .isSameInstanceAs(expectedResult)
                }
        }
    }
    

カスタム コントラクトを作成する

ActivityResultContracts に含まれている事前ビルド済みのさまざまな ActivityResultContract クラスを使用できますが、厳密にタイプセーフな API を必要に応じて提供する独自のコントラクトを作成することもできます。

ActivityResultContract では、入力クラスと出力クラスを定義する必要があります。入力が不要な場合は、入力の型として Void(Kotlin の場合は、Void? または Unit)を使用します。

各コントラクトでは、createIntent() メソッドを実装する必要があります。このメソッドでは、Context と入力を受け取って、startActivityForResult() で使用する Intent を作成します。

各コントラクトでは parseResult() も実装する必要があります。このメソッドでは、指定された resultCodeActivity.RESULT_OKActivity.RESULT_CANCELED など)と Intent から出力を生成します。

createIntent() を呼び出したり、他のアクティビティを開始したり、parseResult() を使用して結果を作成したりせずに、指定された入力に対する結果を特定できる場合は、必要に応じてコントラクトに getSynchronousResult() を実装することができます。

Kotlin

    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)
        }
    }
    

Java

    public class PickRingtone extends ActivityResultContract<Integer, Uri> {
        @NonNull
        @Override
        public Intent createIntent(@NonNull Context context, @NonNull Integer ringtoneType) {
            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
            intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, ringtoneType.intValue());
            return intent;
        }

        @Override
        public Uri parseResult(int resultCode, @Nullable Intent result) {
            if (resultCode != Activity.RESULT_OK || result == null) {
                return null;
            }
            return result.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
        }
    }
    

カスタム コントラクトが不要な場合は、StartActivityForResult コントラクトを使用できます。このコントラクトは、任意の Intent を入力として受け取って ActivityResult を返す汎用コントラクトです。このコントラクトを使用すると、次の例に示すように、コールバックの一部として resultCodeIntent を抽出できます。

Kotlin

    val startForResult = prepareCall(StartActivityForResult()) { result: ActivityResult ->
        if (result.resultCode == Activity.RESULT_OK) {
            val intent = result.intent
            // Handle the Intent
        }
    }

    override fun onCreate(savedInstanceState: Bundle) {
        // ...

        val startButton = findViewById(R.id.start_button)

        startButton.setOnClickListener {
            // Use the Kotlin extension in activity-ktx
            // passing it the Intent you want to start
            startForResult(Intent(this, ResultProducingActivity::class.java))
        }
    }
    

Java

    ActivityResultLauncher<Intent> mStartForResult = prepareCall(new StartActivityForResult(),
            new ActivityResultCallback<ActivityResult>() {
        @Override
        public void onActivityResult(ActivityResult result) {
            if (result.getResultCode() == Activity.RESULT_OK) {
                Intent intent = result.getIntent();
                // Handle the Intent
            }
        }
    });

    @Override
    public void onCreate(@Nullable savedInstanceState: Bundle) {
        // ...

        Button startButton = findViewById(R.id.start_button);

        startButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                // The launcher with the Intent you want to start
                mStartForResult.launch(new Intent(this, ResultProducingActivity.class));
            }
        });
    }