为什么要通过Fragment.setArguments(Bundle)传递参数

Fragment在开发中是经常使用的,我们在创建一个Fragment对象实例的时候一般都会通过new Fragment()构造方法来实现。如果在创建Fragment的时候需要向其传递数据,则可以通过构造方法直接来传递参数,或者通过Fragment.setArguments(Bundle bundle)这种方式来传递参数。这两种参数传递方式大概如下:

方式一:通过构造方法传递参数

在创建Fragment的时候,使用 MyFragment fragment = new MyFragment(parameter) 来传递参数。

public class MyFragment extends Fragment {
    public MyFragment(Parameter p){
        //将参数保存起来
    }
}

方式二:通过Fragment.setArguments(Bundle)传递参数

在创建Fragment的时候,使用MyFragment fragment = MyFragment.newInstance(paramter) 来传递参数。

public class MyFragment extends Fragment {
    public static MyFragment newInstance(Parameter p) {
        MyFragment myFragment = new MyFragment();
        Bundle args = new Bundle();
        args.putInt("someParameter", p);
        myFragment.setArguments(args);
        return myFragment;
    }
}

对比分析

看上去这两种方式没有什么本质的区别,但是通过构造方法传递参数的方式是有隐患的。根据Android文档说明,当一个Fragment重新创建的时候,系统会再次调用Fragment中的默认构造函数,注意是默认构造函数。即,当你创建了一个带有参数的Fragment的之后,一旦由于什么原因(例如横竖屏切换)导致你的Fragment重新创建。那么,很遗憾,你之前传递的参数都不见了,因为recreate你的Fragment的时候,调用的是默认构造函数。因此,官方推荐使用Fragment.setArguments(Bundle bundle)这种方式来传递参数,而不推荐通过构造方法直接来传递参数。

推荐使用Fragment.setArguments(Bundle)传递参数原因

(1)Activity的onCreate(Bundle saveInstance)方法

protected void onCreate(Bundle savedInstanceState) {  
    if (DEBUG_LIFECYCLE ) Slog.v( TAG, "onCreate " + this + ": " + savedInstanceState);  
    if (mLastNonConfigurationInstances != null) {  
        mAllLoaderManagers = mLastNonConfigurationInstances .loaders ;  
    }  
    if (mActivityInfo .parentActivityName != null) {  
        if (mActionBar == null) {  
            mEnableDefaultActionBarUp = true ;  
        } else {  
            mActionBar .setDefaultDisplayHomeAsUpEnabled( true);  
        }  
    }  
    if (savedInstanceState != null) {  
        Parcelable p = savedInstanceState.getParcelable( FRAGMENTS_TAG );  
        mFragments .restoreAllState(p, mLastNonConfigurationInstances != null  
                ? mLastNonConfigurationInstances .fragments : null);  
    }  
    mFragments .dispatchCreate();  
    getApplication().dispatchActivityCreated( this , savedInstanceState);  
    mCalled = true ;  
}  

(2)跟进FragmentManager.restoreAllState()方法

  for (int i=0; i();  
               }  
               if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);  
               mAvailIndices.add(i);  
           }  
} 

(3)跟进FragmentState.instantitate()方法

public Fragment instantiate(Activity 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 ;  
    } 

(4)最终转入到Fragment.instantitate()方法

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);  
   } 

至此,我们可以看到,最终会通过反射无参构造实例化一个新的Fragment,并且给mArgments初始化为原先的值,而原来的Fragment实例的数据都丢失了,并重新进行了初始化。

通过上面的分析,我们可以知道Activity重新创建时,会重新构建它所管理的Fragment,原先的Fragment的字段值将会全部丢失,但是通过Fragment.setArguments(Bundle bundle)方法设置的bundle会保留下来,并在重建时恢复。所以,尽量使用Fragment.setArguments(Bundle bundle)方式来进行参数传递。

注意:setArguments方法的调用必须要在Fragment与Activity关联之前,即setArgument方法的使用必须要在FragmentTransaction 的commit之前使用。

你可能感兴趣的:(为什么要通过Fragment.setArguments(Bundle)传递参数)