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<fms.mActive.length; i++) {
FragmentState fs = fms.mActive[i];
if (fs != null) {
Fragment f = fs.instantiate(mActivity, mParent);
if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
mActive.add(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;
} else {
mActive.add(null);
if (mAvailIndices == null) {
mAvailIndices = new ArrayList<Integer>();
}
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之前使用。