FragmentFactory

Ken Baldauf

Senior Software Engineer @ Acorns

Fragments & FragmentFactory

  • Fragments
    • support configuration changes
      • OS must be able to recreate Fragment
        • handled by FragmentManager
  • Fragments without FragmentFactory
    • empty constructor
      • limitation to support Fragment recreation
  • Fragments with FragmentFactory
    • part of AndroidX
      • requires androidx.fragment:fragment-ktx
    • allows constructor arguments

A Peak Behind The Curtain

/**
 * @deprecated Use {@link FragmentManager#getFragmentFactory()} and
 * {@link FragmentFactory#instantiate(ClassLoader, String)}, manually calling
 * {@link #setArguments(Bundle)} on the returned Fragment.
 */
@Deprecated
@NonNull
public static Fragment instantiate(@NonNull Context context, 
        @NonNull String fname, @Nullable Bundle args)
  • Equivalent to calling empty constructor
  • Deprecated in API 28
  • Default FragmentFactory uses empty constructor 

Transaction w/o FragmentFactory

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)

        supportFragmentManager.beginTransaction()
            .replace(R.id.container, MainFragment.newInstance("session token"))
            .commitNow()
    }
private const val KEY_SESSION_TOKEN = "KEY_SESSION_TOKEN"
class MainFragment : Fragment() {
    private lateinit var session: String

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        session = arguments?.getString(KEY_SESSION_TOKEN) ?: ""
    }

    companion object {
        fun newInstance(session: String): MainFragment {
            return MainFragment().apply {
                arguments = Bundle().apply {
                    putString(KEY_SESSION_TOKEN, session)
                }
            }
        }
    }
}

Transaction w/ FragmentFactory

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        supportFragmentManager.fragmentFactory = FragmentFactoryImpl("session token")
        setContentView(R.layout.main_activity)

        supportFragmentManager.beginTransaction() // null = bundle args
            .replace(R.id.container, MainFragment::class.java, null) 
            .commitNow()
    }
class MainFragment(
    private val session: String
) : Fragment() {

	// contents of fragment
}
val fragment = supportFragmentManager.fragmentFactory
    .instantiate(classLoader, MainFragment::class.java.name)

supportFragmentManager.beginTransaction()
    .replace(R.id.container, fragment)
    .commitNow()

FragmentFactory Implementation

class FragmentFactoryImpl(
    private val session: String
) : FragmentFactory() {
    override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
        return when (className) {
            MainFragment::class.java.name -> MainFragment(session)
            else -> super.instantiate(classLoader, className)
        }
    }
}

Dependency Injection

class MainFragment: Fragment() {
    @Inject lateinit var session: String
	
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val component = DaggerAppComponent.builder().build()
        component.inject(this)
    }
}

DI w/ FragmentFactory

class MainFragment @Inject constructor(
    private val session: String
) : Fragment() {

	// contents of fragment
}

DI w/ FragmentFactory

@Module
abstract class FragmentModule {
    @Binds
    abstract fun bindFragmentFactory(fragmentFactory: FragmentFactoryImpl): FragmentFactory

    @Binds
    @IntoMap
    @FragmentKey(MainFragment::class)
    abstract fun bindMainFragment(mainFragment: MainFragment): Fragment

    @Binds
    @IntoMap
    @FragmentKey(OtherFragment::class)
    abstract fun bindOtherFragment(otherFragment: OtherFragment): Fragment
    
}
fun bindMainFragment(session: String) = MainFragment(session)

DI w/ FragmentFactory

class FragmentFactoryImpl @Inject constructor(
        private val fragmentProviders: Map<Class<out Fragment>, Provider<Fragment>>
) : FragmentFactory() {

    override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
        val fragmentClass = loadFragmentClass(classLoader, className)
        val fragmentProvider = fragmentProviders[fragmentClass]
        return fragmentProvider?.get() ?: super.instantiate(classLoader, className)
    }
}

Questions?

FragmentFactory

By Kenneth Baldauf

FragmentFactory

Taking a look at Android's FragmentFactory.

  • 275