本次排查Bug的fragment基于AndroidX1.1.0
線上bugly報(bào)了一個(gè)Could not find Fragment constructor跪妥,先看一下Fragment的構(gòu)造函數(shù)
/**
* 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();
}
主要看一下注釋說明激挪,如果你想要的使用非默認(rèn)的構(gòu)造函數(shù)碧信,需要自己實(shí)現(xiàn)一個(gè)FragmentFactory去初始化垫卤,然后強(qiáng)烈推薦使用setArguments和getArguments存取參數(shù)。看了一下報(bào)錯(cuò)的類,可以基本確定是使用了帶參數(shù)的構(gòu)造函數(shù)
然后看一下報(bào)錯(cuò)的位置,在Fragment里搜索could not find Fragment constructor
public static Fragment instantiate(@NonNull Context context, @NonNull String fname,
@Nullable Bundle args) {
try {
Class<? extends Fragment> clazz = FragmentFactory.loadFragmentClass(
context.getClassLoader(), fname);
Fragment f = clazz.getConstructor().newInstance();
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.setArguments(args);
}
return f;
} catch (java.lang.InstantiationException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (IllegalAccessException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (NoSuchMethodException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": could not find Fragment constructor", e);
} catch (InvocationTargetException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": calling Fragment constructor caused an exception", e);
}
}
可以看到這里通過反射层玲,調(diào)用了空的構(gòu)造函數(shù)
Fragment f = clazz.getConstructor().newInstance();
但是對(duì)應(yīng)的類沒有空的構(gòu)造函數(shù),所以拋出了這個(gè)異常反症。然后看一下初始的地方辛块,是FragmentActivity在onCreate中調(diào)用的mFragments.restoreSaveState(p);
結(jié)論:
Fragment必須有一個(gè)無參public的構(gòu)造函數(shù),否則在數(shù)據(jù)恢復(fù)的時(shí)候铅碍,會(huì)報(bào)crash
ps:手動(dòng)測(cè)試發(fā)現(xiàn)私有的構(gòu)造函數(shù)無法通過該Constructor.newInstance方法調(diào)用