Android片段工厂方法与构造函数重载

首先,我已经知道FragmentManager经常破坏,然后使用默认构造函数重新创建片段.编码器必须在工厂方法中将重要的事物保存在一束参数中,然后在每次在onCreate(Bundle)中重新生成片段时将它们取出.
public class MyFragment extends Fragment {
    private static final String MY_STRING_CONSTANT = "param";
    private int mIntegerMember;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mIntegerMember= getArguments().getInt(MY_STRING_CONSTANT);
    }
}

我的问题是,这是有区别的:

// Inside MyFragment.java
public MyFragment() {
    // No-argument constructor required by the FragmentManager.
}
public static MyFragment newInstance(int param) {
    // Factory method
    MyFragment fragment = new MyFragment();
    Bundle args = new Bundle();
    args.putInt(MY_STRING_CONSTANT,param);
    fragment.setArguments(args);
    return fragment;
}

// Somewhere else
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.frame_layout,MyFragment.newInstance(123)).commit();

和这个:

// Inside MyFragment.java
public MyFragment() {
    // No-argument constructor required by the FragmentManager.
}
public MyFragment(int param) {
    // Parameterized constructor
    Bundle args = new Bundle();
    args.putInt(MY_STRING_CONSTANT,param);
    setArguments(args);
}

// Somewhere else
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.frame_layout,new MyFragment(123)).commit();

我看不到什么可以阻止FragmentManager调用无参数的构造函数.并且在保存参数化构造函数(Bundle对象)中的数据将在onCreate()期间保留并恢复,就像使用工厂方法一样.

解决方法

Android从未直接调用非默认构造函数(也不是工厂方法) – 从技术上讲,使用它并不重要.您可以在添加Fragment之前随时调用setArguments(在任意方法中,即使在构造函数中),如果重新创建Fragment,则该bundle将被保存/还原.视图还具有由Android调用的特殊构造函数,但如果需要,您可以自由使用任意参数(它们不会被Android调用).

Fragment.setArguments代码:

/**
 * Supply the construction arguments for this fragment.  This can only
 * be called before the fragment has been attached to its activity; that
 * is,you should call it immediately after constructing the fragment.  The
 * arguments supplied here will be retained across fragment destroy and
 * creation.
 */
public void setArguments(Bundle args) {
    if (mIndex >= 0) {
        throw new IllegalStateException("Fragment already active");
    }
    mArguments = args;
}

片段代码:

/**
     * 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,e);
        } catch (IllegalAccessException e) {
            throw new InstantiationException("Unable to instantiate fragment " + fname
                    + ": make sure class name exists,e);
        }
    }

当Android要创建片段的实例时,将调用Fragment.instantiate.它通过Class.newInstance简单的调用,它是使用默认的零arg构造函数创建一个类的Java方法.看这个代码,似乎没有问题,创建额外的构造函数和在其中调用setArguments.

作为惯例,使用片段时通常使用工厂方法.大多数official sample Fragment code也采用工厂方法.以下是一些可能的原因:

>如果您正在编写一个自定义构造函数(带有参数),那么您也必须指定一个零对象构造函数.一个常见的错误是创建一个自定义构造函数,但是忘记定义一个零对象构造函数 – 当Android尝试在重新创建片段时尝试调用零对象构造函数,这将导致崩溃.>创建自定义构造函数时,您可能会尝试直接将构造函数参数分配给字段.这是任何其他Java类写的几乎相当(因此你将如何自然地想写类).由于Android只会调用片段上的零对象构造函数,所以这些数据将无法用于任何重新创建的实例.如你所知,使用setArguments是解决这个问题的方法.即使您可以在构造函数中执行此操作,使用工厂方法使得更加明显的是,此类不能以正常方式构造,从而减少了上述错误(或类似情况)的可能性.

相关文章

Android 如何解决dialog弹出时无法捕捉Activity的back事件 在...
Android实现自定义带文字和图片的Button 在Android开发中经常...
Android 关于长按back键退出应用程序的实现最近在做一个Andr...
android自带的时间选择器只能精确到分,但是对于某些应用要求...