本文主要分析FragmentFactory编写前后对比,以及简单的FragmentFactory使用
简述
Fragment constructor injection is now -and has been for a while- supported in Android thanks to FragmentFactory. While it isn’t an API developers have to use, it can be regarded as a better design approach in certain situations and can help when testing Fragments with external dependencies.[1]
FragmentFactory主要用于Fragment的初始化,但是FragmentFactory并非强制使用的.默认无参的Fragment构造函数可以不使用FragmentFactory,否则还是建议使用FragmentFactory来进行实例化.[2]
源码分析
FragmentFactory最重要的方法 FragmentFactory#instantiate(ClassLoader, String) ,源码方法很短.
/**
* Create a new instance of a Fragment with the given class name. This uses
* {@link #loadFragmentClass(ClassLoader, String)} and the empty
* constructor of the resulting Class by default.
*
* @param classLoader The default classloader to use for instantiation
* @param className The class name of the fragment to instantiate.
* @return Returns a new fragment instance.
* @throws Fragment.InstantiationException If there is a failure in instantiating
* the given fragment class. This is a runtime exception; it is not
* normally expected to happen.
*/
@NonNull
public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) {
try {
Class<? extends Fragment> cls = loadFragmentClass(classLoader, className);
return cls.getConstructor().newInstance();
} catch (java.lang.InstantiationException e) {
throw new Fragment.InstantiationException("Unable to instantiate fragment " + className
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (IllegalAccessException e) {
throw new Fragment.InstantiationException("Unable to instantiate fragment " + className
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (NoSuchMethodException e) {
throw new Fragment.InstantiationException("Unable to instantiate fragment " + className
+ ": could not find Fragment constructor", e);
} catch (InvocationTargetException e) {
throw new Fragment.InstantiationException("Unable to instantiate fragment " + className
+ ": calling Fragment constructor caused an exception", e);
}
}
使用方法就是继承FragmentFactory之后重写 FragmentFactory#instantiate(ClassLoader, String)即可.[1]
//demo code
class CustomFragmentFactory(private val dependency: Dependency) : FragmentFactory() {
override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
if (className == CustomFragment::class.java.name) {
return CustomFragment(dependency)
}
return super.instantiate(classLoader, className)
}
}
为什么会有FragmentFactory?
没有FragmentFactory之前,是简单直接粗暴调用Fragment#instantiate(@NonNull Context context, @NonNull String fname)就算. 不过很多时候开发者写的Fragment都会写一个static的newInstance(params:object)方法去实例化,当状态恢复的时候,直接调用Fragment#instantiate(@NonNull Context context, @NonNull String fname)就会出错 -- InstantiationException,所以才有了FragmentFactory+FragmentManager#getFragmentFactory()的搭配使用.
/**
* Like {@link #instantiate(Context, String, Bundle)} but with a null
* argument Bundle.
* @deprecated Use {@link FragmentManager#getFragmentFactory()} and
* {@link FragmentFactory#instantiate(ClassLoader, String)}
*/
@SuppressWarnings("deprecation")
@Deprecated
@NonNull
public static Fragment instantiate(@NonNull Context context, @NonNull String fname) {
return instantiate(context, fname, null);
}
用法
如下图所示,可以通过给FragmentManager 传入一个FragmentFactory的对象(这里有个坑,下面会说到),之后对接下来的Fragment做带参实例化处理,如果Fragment可以无参实例化的话,可以不使用FragmentFactory.
然后在需要的地方调起fm.getFragmentFactory().instantiate(context.getClassLoader(), name),
e.g. FragmentManager#restoreSaveState(state),FragmentContainerView(context,attrs,fm),FragmentTabHost#doTabChanged( tag, FragmentTransaction)

问题
上面提到,其实每个 FragmentManager 只有一个 FragmentFactory 的对象,而 activity 也是只有一个 FragmentManager. 所以如果是那种[一个Activity多个业务Fragment]的模式,而且业务Fragment都在不同module以及不同framework的话,就不能够每个framework设置一个 FragmentFactory 了,否则就能够给其中一个framework使用了.如果打算每个framework设置一个 FragmentFactory ,之后Activity的 FragmentFactory 遍历各个framework的 FragmentFactory 也是不行的, 因为 FragmentFactory 的源码决定了, 如果找不到Fragment就会报错 , 当然也可以在 else 的时候,不调用 super.instantiate(classLoader, className) ,不过这样在团队开发的时候容易出错,造成APP crash.
为了解决这个问题,只能够把 FragmentFactory 放到Activity做扩展,在各个framework实现不同的delegate.

