Android爬坑-Fragment 构造
来源:互联网 发布:淘宝怎么互刷 编辑:程序博客网 时间:2024/06/03 20:51
最近,在做一个项目。当app启动后,然后使其进入后台进程(按home键),接着使用其它app(用其它app的目的是为了让系统内存不足,然后让系统将我们的app杀死)。当我们的app被系统杀死后,这时候通过任务管理点击我们的app进入应用。这时候问题出现了,app崩溃了,为了不暴露项目,一些项目包名或者类名的信息就省略了,下面就是异常的关键信息:
[plain] view plain copy
在CODE上查看代码片派生到我的代码片
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 is public at 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 that is public at 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 more Caused 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对应的关键代码片
[java] view plain copy
在CODE上查看代码片派生到我的代码片
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(),
[java] view plain copy
在CODE上查看代码片派生到我的代码片
/** * 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吧,
[plain] view plain copy
在CODE上查看代码片派生到我的代码片
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 class the 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文档上的一段描述:
[plain] view plain copy
在CODE上查看代码片派生到我的代码片
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的关键所在。
- Android爬坑-Fragment 构造
- Android Fragment 构造函数
- Android Fragment的构造函数
- android中Fragment的构造函数
- [Android]Fragment源码分析(一) 构造
- android fragment 构造函数非public
- android中Fragment的构造函数
- android关于fragment的构造函数用法建议
- android Fragment里的newInstance和构造函数
- Android中Fragment的构造函数 his fragment should provide a default constructor
- Fragment的构造函数
- Fragment的构造函数
- Android Fragment---创建Fragment
- Android Fragment---管理Fragment
- Android Fragment---创建Fragment
- Android Fragment---管理Fragment
- Fragment源码分析(一) 构造
- android开发步步为营之53:viewpager+fragment构造可左右滑动标签页效果
- C#开发中三层架构BLL,DAL还有IBLL和IDAL接口,请问为什么要定义接口?有什么用啊?
- 14D
- 数据库对象及其操作
- 自定义透明的Dialog
- Centos 6.5下SVN服务安装配置
- Android爬坑-Fragment 构造
- ue4 NewObject
- 基于zabbix自动化线上实战 基础篇 -- Nginx监控详解
- 【转】做人不装逼,和咸鱼有什么区别——16个装逼又实用的网站(程序员向)
- openLTE中的RRC层
- Linux下tomcat启动不了的解决方法
- RAID
- MySQL中的表、字段的约束修改
- 关于web.xml的问题