• Android问题笔记 - NoSuchmethodException: could not find Fragment constructor


    👉关于作者

    专注于Android/Unity和各种游戏开发技巧,以及各种资源分享(网站、工具、素材、源码、游戏等)
    有什么需要欢迎底部卡片私我,交流让学习不再孤单

    在这里插入图片描述

    👉实践过程

    😜解决

    今天突然收到一个异常的崩溃信息:
    在这里插入图片描述
    解决方案其实很简单:

    1. Fragemnt需要一个无参的构造参数,且是public类型的
    2. 多人合作开发项目要注意这个构造参数是不是 public 类型

    😜结论

    总结一句话就是:这个错误原因是 Fragment 的有参构造函数上。当 Fragment 因为某种原因(如横竖屏切换)重新创建时,会调用到onCreate方法传入之前保存的状态,在instantiate方法中通过反射无参构造函数创建一个Fragment,并且为Arguments初始化为原来保存的值,而此时如果没有无参构造函数就会抛出异常,造成程序崩溃。

    所以我们强烈推荐使用setArguments和getArguments进行存取参数。

    😜原因

    知道怎么解决我们就看看出现这个问题的原因是什么:
    我们先来看一下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 {@link #setArguments}
     * and later retrieved by the Fragment with {@link #getArguments}.
     *
     * 

    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() { }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    注释说明:
    默认构造器。
    每一个Fragment必须有一个无参的构造函数,以便当Activity恢复状态时fragment可以实例化。
    强烈建议fragment的子类不要有其他的有参构造函数,因为当fragment重新实例化时不会调用这些有参构造函数;
    如果要传值应该使用setArguments方法,在需要获取这些值时调用getArguments方法。

    注释力明确说明了使用有参构造函数会出问题,建议使用无参构造函数,但是并没有告诉我们具体是哪里的问题。我们在Fragment中直接Ctrl + F 搜索 Unable to instantiate fragment,发现抛出异常的地方反射无参构造方法,在instantiate方法中抛出的。

    public static Fragment instantiate(Context context, String fname, @Nullable 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.getConstructor().newInstance();
            if (args != null) {
                args.setClassLoader(f.getClass().getClassLoader());
                f.setArguments(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 (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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    仔细看代码分析分析,Fragment 实例化通过调用类对象的getConstructor()方法获取构造器对象并调用了newInstance()方法创建对象。此时还会将args参数设置给Fragment。具体报错的地方我们是找到了,但是这个方法是在哪里调用触发的呢?在Fragment没有找到调用的地方,由于Fragment是由FragmentManager管理的,在该类发现是在restoreAllState方法中调用的。

    void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
            // Build the full list of active fragments, instantiating them from
            // their saved state.
            mActive = new SparseArray<>(fms.mActive.length);
            for (int i=0; i<fms.mActive.length; i++) {
                FragmentState fs = fms.mActive[i];
                if (fs != null) {
                    FragmentManagerNonConfig childNonConfig = null;
                    if (childNonConfigs != null && i < childNonConfigs.size()) {
                        childNonConfig = childNonConfigs.get(i);
                    }
                    ViewModelStore viewModelStore = null;
                    if (viewModelStores != null && i < viewModelStores.size()) {
                        viewModelStore = viewModelStores.get(i);
                    }
                    Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig,
                            viewModelStore);
                    if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
                    mActive.put(f.mIndex, f);
                    // Now that the fragment is instantiated (or came from being
                    // retained above), clear mInstance in case we end up re-restoring
                    // from this FragmentState again.
                    fs.mInstance = null;
                }
            }
        ...   
        }
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    这方法名意为恢复所有的状态,而其中注释为创建激活Fragment的列表,并将他们从保存的状态中实例化。这个方法应该是Fragment重新实例化时调用的方法。
    至此我们就知道什么原因了。

    👉其他

    📢作者:小空和小芝中的小空
    📢转载说明-务必注明来源:https://zhima.blog.csdn.net/
    📢这位道友请留步☁️,我观你气度不凡,谈吐间隐隐有王者霸气💚,日后定有一番大作为📝!!!旁边有点赞👍收藏🌟今日传你,点了吧,未来你成功☀️,我分文不取,若不成功⚡️,也好回来找我。

    温馨提示点击下方卡片获取更多意想不到的资源。
    空名先生

  • 相关阅读:
    神经网络怎么看训练效果,神经网络结果图如何看
    聚观早报 | 苹果和微软差了一个特斯拉;腾讯会议开始对个人收费
    C#8.0本质论第十章--合式类型
    ios 16更新内容来了,快来看看有什么变化吧
    【PyCharm】安装第三方库
    arthas查看spring bean及调用其方法
    java-net-php-python-32jspm手机销售网站程序计算机毕业设计程序
    redis(一)
    (二)Celery: 分布式异步消息任务队列
    DGIOT国内首家轻量级物联网开源平台——MQTT接入实战教程
  • 原文地址:https://blog.csdn.net/qq_27489007/article/details/133942417