Android Fragment Kotlin Java

本週三去參加 Android Developer 開發讀書會 主辦的 Android Code Club

2020-08-19 Week 3 - Activity and Fragment

當天講者有提到說,如果 Fragment 被系統回收,系統再次生成這一個 fragment 只會用預設的 initial

這是一個很特別的情境,通常不會在其他語言也遇到,所以寫一篇文章來記錄整個狀況

=== 這是分隔線 ===

如我們剛才所說,系統只會用 default 的 initial func 去建立 fragment

可以在 Fragment 的 class Source code 裡面看到

It is strongly recommended to supply arguments with {@link #setArguments} * and later retrieved by the Fragment with {@link #getArguments}. These arguments * are automatically saved and restored alongside the Fragment.

  /**
    * Constructor used by the default {@link FragmentFactory}. You must
    * {@link FragmentManager#setFragmentFactory(FragmentFactory) set a custom FragmentFactory}
    * if you want to use a non-default constructor to ensure that your constructor
    * is called when the fragment is re-instantiated.
    *
    * <p>It is strongly recommended to supply arguments with {@link #setArguments}
    * and later retrieved by the Fragment with {@link #getArguments}. These arguments
    * are automatically saved and restored alongside the Fragment.
    *
    * <p>Applications should generally not implement a constructor. Prefer
    * {@link #onAttach(Context)} instead. It is the first place application code can run where
    * the fragment is ready to be used - the point where the fragment is actually associated with
    * its context. Some applications may also want to implement {@link #onInflate} to retrieve
    * attributes from a layout resource, although note this happens when the fragment is attached.
    */
  public Fragment() {
      initLifecycle();
  }

既然官方強烈的建議我們要這樣做,那就照著做比較沒問題


通常我們為了方便,當然會建立 BaseFragment 做好一些事,方便使用

也會使用 kotlin 的特性,保證某些變數必然要被傳進來

like this

abstract class TestBaseFragment(@LayoutRes private val layout: Int) : Fragment() {
    // Do Something
}

class TestSubFragment private constructor(private val id: String) : TestBaseFragment(
    R.layout.xxxx
) {

    companion object {
        fun newInstance(id: String) = TestSubFragment(id = id)
    }
}

所以,這樣的寫法,在 Fragment 被系統回收後再生,就會因為 id 沒有值,出現非我們期望的結果

甚至是 Crash

為了避免出現非我們預期的結果,或者是 Crash,我們就需要換種寫法

class TestSubFragment private constructor() : TestBaseFragment(
    R.layout.xxxx
) {
    private val id: String = arguments?.getString("id", "") ?: ""

    companion object {
        fun newInstance(id: String): TestSubFragment {
            val f = TestSubFragment()
            val args = Bundle()
            args.putString("id", id)
            f.arguments = args
            return f
        }
    }
}

這樣就可以避開這一個奇怪的坑(Android 真是一個神奇的平台)

打完收工!