2021年1月8日金曜日

<fragment> と FragmentContainerView では Fragment のライフサイクルメソッドの呼ばれるタイミングが違う

動作確認は androidx.fragment:fragment-ktx:1.2.5 でしています。今後のバージョンで動作が変わる可能性があります。

fragment-ktx:1.2.5 で <fragment> を使うと FragmentContainerView を使うようにメッセージがでます。


このメッセージに従って FragmentContainerView に変えたとして、大体は問題ないと思いますが、問題が起こる場合もあります。

<fragment> と FragmentContainerView で Fragment のライフサイクルメソッドの呼ばれる順番を確認してみます。
  1. class MainActivity : AppCompatActivity() {  
  2.   
  3.     override fun onCreate(savedInstanceState: Bundle?) {  
  4.         super.onCreate(savedInstanceState)  
  5.         setContentView(R.layout.activity_main)  
  6.   
  7.         val f = supportFragmentManager  
  8.             .findFragmentById(R.id.mainFragment) as MainFragment  
  9.   
  10.         Log.d("MainActivity""onCreate : ${f.view}")  
  11.     }  
  12. }  
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <fragment xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:id="@+id/mainFragment"  
  4.     android:name="yanzm.sample.myapplication.MainFragment"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent" />  
  1. class MainFragment : Fragment() {  
  2.   
  3.     override fun onAttach(context: Context) {  
  4.         super.onAttach(context)  
  5.         Log.d("MainFragment""onAttach")  
  6.     }  
  7.   
  8.     override fun onCreate(savedInstanceState: Bundle?) {  
  9.         super.onCreate(savedInstanceState)  
  10.         Log.d("MainFragment""onCreate")  
  11.     }  
  12.   
  13.     override fun onCreateView(  
  14.         inflater: LayoutInflater,  
  15.         container: ViewGroup?,  
  16.         savedInstanceState: Bundle?  
  17.     ): View {  
  18.         Log.d("MainFragment""onCreateView")  
  19.         return View(inflater.context)  
  20.     }  
  21.   
  22.     override fun onActivityCreated(savedInstanceState: Bundle?) {  
  23.         super.onActivityCreated(savedInstanceState)  
  24.         Log.d("MainFragment""onActivityCreated")  
  25.     }  
  26.   
  27.     override fun onStart() {  
  28.         super.onStart()  
  29.         Log.d("MainFragment""onStart")  
  30.     }  
  31.   
  32.     override fun onResume() {  
  33.         super.onResume()  
  34.         Log.d("MainFragment""onResume")  
  35.     }  
  36. }  
↑ の <fragment> の場合のコードを実行すると次のようになります。
  1. D/MainFragment: onAttach  
  2. D/MainFragment: onCreate  
  3. D/MainFragment: onCreateView  
  4. D/MainActivity: onCreate : android.view.View{7009652 V.ED..... ......I. 0,0-0,0 #7f0800ca app:id/mainFragment}  
  5. D/MainFragment: onActivityCreated  
  6. D/MainFragment: onStart  
  7. D/MainFragment: onResume  
Fragment の onAttach(), onCreate(), onCreateView() が呼ばれた後に Activity の onCreate() が呼ばれています。そのため、Activity の onCreate() で Fragment の View にアクセスすると null ではありません。


では FragmentContainerView に変えて見てみましょう。タグ以外は変更なしです。
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:id="@+id/mainFragment"  
  4.     android:name="yanzm.sample.myapplication.MainFragment"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent" />  
  1. D/MainFragment: onAttach  
  2. D/MainFragment: onCreate  
  3. D/MainActivity: onCreate : null  
  4. D/MainFragment: onCreateView  
  5. D/MainFragment: onActivityCreated  
  6. D/MainFragment: onStart  
  7. D/MainFragment: onResume  
なんと Fragment の onCreateView() の呼ばれるタイミングが変わっています。 Activity の onCreate() の後に呼ばれるように変わってしまいました。そのため Activity の onCreate() で Fragment の View にアクセスすると null になっています。

Activity の onCreate() の時点で Fragment の onCreateView() がすでに呼ばれている前提の処理だと、FragmentContainerView に変更したときに意図しない動きになってしまうので注意が必要です。