浅析Fragment为什么需要Public的empty constructor
来源:互联网 发布:网易云信PHP 编辑:程序博客网 时间:2024/05/11 00:02
最近,在做一个项目。当app启动后,然后使其进入后台进程(按home键),接着使用其它app(用其它app的目的是为了让系统内存不足,然后让系统将我们的app杀死)。当我们的app被系统杀死后,这时候通过任务管理点击我们的app进入应用。这时候问题出现了,app崩溃了,为了不暴露项目,一些项目包名或者类名的信息就省略了,下面就是异常的关键信息:
java.lang.RuntimeException: Unable to start activity ComponentInfo{省略}: android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment 省略:make sure class name exists, is public, and has an empty constructor that ispublicat android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1750)at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1766)at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:2960)at android.app.ActivityThread.access$1600(ActivityThread.java:127)at android.app.ActivityThread$H.handleMessage(ActivityThread.java:945)at android.os.Handler.dispatchMessage(Handler.java:99)at android.os.Looper.loop(Looper.java:130)at android.app.ActivityThread.main(ActivityThread.java:3818)at java.lang.reflect.Method.invokeNative(Native Method)at java.lang.reflect.Method.invoke(Method.java:507)at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:875)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:633)at dalvik.system.NativeStart.main(Native Method)Caused by: android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment 省略: make sure class name exists, is public, and has an empty constructor thatis publicat android.support.v4.app.Fragment.instantiate(Fragment.java:399)at android.support.v4.app.FragmentState.instantiate(Fragment.java:97)at android.support.v4.app.FragmentManagerImpl.restoreAllState(FragmentManager.java:1760)at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:200)at 省略at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1710)... 12 moreCaused by: java.lang.InstantiationException: <pre code_snippet_id="587294" snippet_file_name="blog_20150123_1_3548387" name="code" class="plain">省略at java.lang.Class.newInstanceImpl(Native Method)at java.lang.Class.newInstance(Class.java:1409)at android.support.v4.app.Fragment.instantiate(Fragment.java:388)... 18 more从上面的关键信息可以看出,异常的原因就是因为使用的fragment没有public的empty constructor。事实,也确实如此,我的那个fragment有一个带参数的constructor但是i没有Public的empty constructor。
那么问题来了,为什么Fragment必须要empty constructor?
首先看到这个异常栈,就能发现问题是由于,fragment在还原状态中调用FragmentState#instantitae()->Fragment#instantitae()抛出异常,那么就顺着这个调用点,进入源代码。
首先,看下FragmentState对应的关键代码片
public Fragment instantiate(FragmentActivity activity, Fragment parent) { if (mInstance != null) { return mInstance; } if (mArguments != null) { mArguments.setClassLoader(activity.getClassLoader()); } mInstance = Fragment.instantiate(activity, mClassName, mArguments); if (mSavedFragmentState != null) { mSavedFragmentState.setClassLoader(activity.getClassLoader()); mInstance.mSavedFragmentState = mSavedFragmentState; } mInstance.setIndex(mIndex, parent); mInstance.mFromLayout = mFromLayout; mInstance.mRestored = true; mInstance.mFragmentId = mFragmentId; mInstance.mContainerId = mContainerId; mInstance.mTag = mTag; mInstance.mRetainInstance = mRetainInstance; mInstance.mDetached = mDetached; mInstance.mFragmentManager = activity.mFragments; if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG, "Instantiated fragment " + mInstance); return mInstance; }很明显,在上述代码片中,一眼就找到了方法调用mInstance = Fragment.instantiate(activity, mClassName, mArguments),你猜的没错,它就是fragment的实例对象赋值。顺着这个点进入Fragment#instantiate(),
/** * Create a new instance of a Fragment with the given class name. This is * the same as calling its empty constructor. * * @param context The calling context being used to instantiate the fragment. * This is currently just used to get its ClassLoader. * @param fname The class name of the fragment to instantiate. * @param args Bundle of arguments to supply to the fragment, which it * can retrieve with {@link #getArguments()}. May be null. * @return Returns a new fragment instance. * @throws InstantiationException If there is a failure in instantiating * the given fragment class. This is a runtime exception; it is not * normally expected to happen. */ public static Fragment instantiate(Context context, String fname, Bundle args) { try { Class<?> clazz = sClassMap.get(fname); if (clazz == null) { // Class not found in the cache, see if it's real, and try to add it clazz = context.getClassLoader().loadClass(fname); sClassMap.put(fname, clazz); } Fragment f = (Fragment)clazz.newInstance(); if (args != null) { args.setClassLoader(f.getClass().getClassLoader()); f.mArguments = args; } return f; } catch (ClassNotFoundException 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 (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语句块,也能看出,是不是与之前说的异常信息一模一样。异常抛出确实是来源于此,上述代码片的关键,其实就是通过java的反射机制进行实例化Fragment。好,即使对Java反射机制不太了解也没什么关系,顺着思路来。其中Fragment f = (Fragment)clazz.newInstance()就是关键中的关键,先来看下Class#newInstance()的java doc吧,
public T newInstance() throws InstantiationException, IllegalAccessException Creates a new instance of the class represented by this Class object. The class is instantiated as if by a new expression with an empty argument list. The class is initialized if it has not already been initialized. Note that this method propagates any exception thrown by the nullary constructor, including a checked exception. Use of this method effectively bypasses the compile-time exception checking that would otherwise be performed by the compiler. The Constructor.newInstance method avoids this problem by wrapping any exception thrown by the constructor in a (checked) InvocationTargetException.Returns: a newly allocated instance of the class represented by this object.Throws: IllegalAccessException - if the class or its nullary constructor is not accessible. InstantiationException - if this Class represents an abstract class, an interface, an array class, a primitive type, or void; or if the class has no nullary constructor; or if the instantiation fails for some other reason. ExceptionInInitializerError - if the initialization provoked by this method fails. SecurityException - If a security manager, s, is present and any of the following conditions is met:invocation of s.checkMemberAccess(this, Member.PUBLIC) denies creation of new instances of this classthe caller's class loader is not the same as or an ancestor of the class loader for the current class and invocation of s.checkPackageAccess() denies access to the package of this class
主要看下InstantiationException和IllegalAccessException,对于我们来说其关键信息就是:
- InstantiationException,如果类没有empty constructor,该异常就会抛出。
- IllegalAccessException,如果类没有public的empty constructor,该异常就会抛出。
为了佐证上述分析的正确性,找到了Fragment API文档上的一段描述:
public Fragment ()Default constructor. Every fragment must have an empty constructor, so it can be instantiated when restoring its activity's state. It is strongly recommended that subclasses do not have other constructors with parameters, since these constructors will not be called when the fragment is re-instantiated; instead, arguments can be supplied by the caller with setArguments(Bundle) and later retrieved by the Fragment with getArguments().Applications should generally not implement a constructor. The first place application code an run where the fragment is ready to be used is in onAttach(Activity), the point where the fragment is actually associated with its activity. Some applications may also want to implement onInflate(Activity, AttributeSet, Bundle) to retrieve attributes from a layout resource, though should take care here because this happens for the fragment is attached to its activity.
总结:当系统因为内存紧张杀死非前台进程(并非真正的杀死),然后我们将被系统杀掉的非前台app带回前台,如果这个时候有UI是呈现在Fragment中,那么会因为restore造成fragment需要通过反射实例对象,从而将之前save的状态还原,而这个反射实例对象就是fragment需要Public的empty constructor的关键所在。
4 0
- 浅析Fragment为什么需要Public的empty constructor
- 浅析Fragment为什么需要空的构造方法
- Unable to instantiate fragment make sure class name exists, is public, and has an empty constructor
- Unable to instantiate fragment make sure class name exists, is public, and has an empty constructor
- 有关于Fragment是否应该有empty constructor的问题
- Error:Error: This fragment should provide a default constructor (a public constructor打包apk错误
- This fragment should provide a default constructor (a public constructor with no arguments
- 关于make sure class name exists, is public, and has an empty constructor that is public解决办法
- Fragment为什么需要一个默认的空的构造器
- 在原型继承的时候为什么需要对constructor属性就行修正
- fragment的浅析
- 为什么返回值优化需要显示的提供一个子copy constructor,,,但是又不调用它呢?
- PHP empty(),你需要小心的函数
- 为什么需要public static void main(String[] args)这个方法
- Unable to instantiate fragment XXX make sure class name exists, is public, and has an empty constru
- fragment 嵌套 fragment 需要注意的地方
- java.lang.InstantiationException:no empty constructor解决方法
- AQGridView开源库中的示例DEMO:SpringBoard中为什么需要empty cell
- SAP ERP差异来源和差异处理
- lua 格式化输出字符串
- C编译器剖析_2.1 从Makefile走起
- 两台Exadata搭建RAC+DG
- Java与MySQL之间Blob数据转换
- 浅析Fragment为什么需要Public的empty constructor
- 158个JAVA免豆精品资料汇总
- ObjectOutputStream 对象追加的实现
- sina xss
- SQL Server 跨服务器 不同数据库之间复制表的数据
- 好吧,你出名了,【收藏博客网址】
- 二叉树的创建和前序遍历
- iOS课程表
- DUMP 文件收集时的几个注意事项