startActivityForResultの非推奨化について - シンプルな方法


まず、startActivityForResultメソッドが非推奨となった理由を見てみましょう。このメソッドは、別のアクティビティを起動し、そのアクティビティから結果を受け取るために使用されました。しかし、このアプローチは古くなり、新しいアーキテクチャーパターンである「Jetpack Navigation Component」と組み合わせることが推奨されています。

Jetpack Navigation Componentを使用すると、画面間のナビゲーションをより簡単に管理できます。まず、Jetpack Navigationライブラリをプロジェクトに追加します。build.gradleファイルに依存関係を追加しましょう。

dependencies {
    def nav_version = "2.4.0"
    // Navigation
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}

次に、ナビゲーショングラフを作成します。resフォルダ内にnavigationディレクトリを作成し、nav_graph.xmlという名前のファイルを作成します。このファイルには、アクティビティ間のナビゲーションのフローを定義します。

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    app:startDestination="@id/firstFragment">
    <fragment
        android:id="@+id/firstFragment"
        android:name="com.example.myapp.FirstFragment"
        android:label="First Fragment">
        <action
            android:id="@+id/action_firstFragment_to_secondFragment"
            app:destination="@id/secondFragment" />
    </fragment>
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.example.myapp.SecondFragment"
        android:label="Second Fragment">
        <action
            android:id="@+id/action_secondFragment_to_thirdFragment"
            app:destination="@id/thirdFragment" />
    </fragment>
    <fragment
        android:id="@+id/thirdFragment"
        android:name="com.example.myapp.ThirdFragment"
        android:label="Third Fragment" />
</navigation>

ナビゲーショングラフが作成されたら、アクティビティでNavHostFragmentを使用してナビゲーションを実行できます。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val navController = findNavController(R.id.nav_host_fragment)
        NavigationUI.setupActionBarWithNavController(this, navController)
    }
    override fun onSupportNavigateUp(): Boolean {
        val navController = findNavController(R.id.nav_host_fragment)
        return navController.navigateUp() || super.onSupportNavigateUp()
    }
}

これで、アクティビティ間のナビゲーションが構成されました。次に、結果を取得する方法を見てみましょう。

たとえば、FirstFragmentからSecondFragmentに移動し、結果を受け取る場合を考えます。まず、SecondFragmentに遷移するアクションをナビゲーショングラフで定義します。

<fragment
    android:id="@+id/firstFragment"
    android:name="com.example.myapp.FirstFragment"
    android:label="First Fragment">
    <action
        android:id="@+id/action_firstFragment_to_secondFragment"
        app:destination="@id/secondFragment"
        app:launchSingleTop="true"
        app:popUpTo="@id/firstFragment"
        app:popUpToInclusive="true" />
</fragment>
<fragment
    android:id="@+id/secondFragment"
    android:name="com.example.myapp.SecondFragment"
    android:label="Second Fragment">
    <argument
        android:name="myResult"
        app:argType="string" />
    <action
        android:id="@+id/action_secondFragment_to_thirdFragment"
        app:destination="@id/thirdFragment" />
</fragment>

ここで、app:launchSingleTop="true"とapp:popUpTo="@id/firstFragment"の設定が重要です。これにより、SecondFragmentが既にスタックに存在する場合は再生成せず、戻るボタンでFirstFragmentに戻ることができます。

次に、FirstFragmentからSecondFragmentに遷移するコードを実装します。

val action = FirstFragmentDirections.actionFirstFragmentToSecondFragment()
findNavController().navigate(action)

また、SecondFragmentで結果を受け取るためのコードを追加します。

val navController = findNavController()
val result = "This is the result"
val bundle = bundleOf("myResult" to result)
navController.previousBackStackEntry?.savedStateHandle?.set("myResult", bundle)
navController.popBackStack()

これで、結果を受け取る準備が整いました。FirstFragmentに戻り、結果を取得するために以下のコードを追加します。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val navController = findNavController()
    val resultObserver = LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_RESUME && navController.currentBackStackEntry?.savedStateHandle?.contains("myResult") == true) {
            val result = navController.currentBackStackEntry?.savedStateHandle?.get<Bundle>("myResult")
            val myResult = result?.getString("myResult")
            // 結果を使用する処理をここに追加します
        }
    }
    viewLifecycleOwner.lifecycle.addObserver(resultObserver)
}