Android 中为什么要用Fragment.setArguments(Bundle bundle)来传递参数
Fragment在Android3.0开始提供,并且在兼容包中也提供了Fragment特性的支持。Fragment的推出让我们编写和管理用户界面更快捷更方便了。
但当我们实例化自定义Fragment时,为什么官方推荐Fragment.setArguments(Bundlebundle)这种方式来传递参数,而不推荐通过构造方法直接来传递参数呢?为了弄清这个问题,我们可以做一个测试,分别测试下这两种方式的不同
首先,我们来测试下通过构造方法传递参数的情况
publicclassFramentTestActivityextendsActionBarActivity{
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState==null){
getSupportFragmentManager().beginTransaction()
.add(R.id.container,newTestFragment("param")).commit();
}
}
publicstaticclassTestFragmentextendsFragment{
privateStringmArg="non-param";
publicTestFragment(){
Log.i("INFO","TestFragmentnon-parameterconstructor");
}
publicTestFragment(Stringarg){
mArg=arg;
Log.i("INFO","TestFragmentconstructwithparameter");
}
@Override
publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,
BundlesavedInstanceState){
ViewrootView=inflater.inflate(R.layout.fragment_main,container,
false);
TextViewtv=(TextView)rootView.findViewById(R.id.tv);
tv.setText(mArg);
returnrootView;
}
}
}
可以看到我们传递过来的数据正确的显示了,现在来考虑一个问题,如果设备配置参数发生变化,这里以横竖屏切换来说明问题,显示如下
发生了什么问题呢?我们传递的参数哪去了?为什么会显示默认值?不急着讨论这个问题,接下来我们来看看Fragment.setArguments(Bundlebundle)这种方式的运行情况
publicclassFramentTest2ActivityextendsActionBarActivity{
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState==null){
getSupportFragmentManager().beginTransaction()
.add(R.id.container,TestFragment.newInstance("param")).commit();
}
}
publicstaticclassTestFragmentextendsFragment{
privatestaticfinalStringARG="arg";
publicTestFragment(){
Log.i("INFO","TestFragmentnon-parameterconstructor");
}
publicstaticFragmentnewInstance(Stringarg){
TestFragmentfragment=newTestFragment();
Bundlebundle=newBundle();
bundle.putString(ARG,arg);
fragment.setArguments(bundle);
returnfragment;
}
@Override
publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,
BundlesavedInstanceState){
ViewrootView=inflater.inflate(R.layout.fragment_main,container,
false);
TextViewtv=(TextView)rootView.findViewById(R.id.tv);
tv.setText(getArguments().getString(ARG));
returnrootView;
}
}
}
我们再来看看横竖屏切换后的运行情况
看到了吧,我们传递的参数在横竖屏切换的情况下完好保存了下来,正确的显示给用户
那么这到底是怎么回事呢,我们知道设备横竖屏切换的话,当前展示给用户的Activity默认情况下会重新创建并展现给用户,那依附于Activity的Fragment会进行如何处理呢,我们可以通过源码来查看
先来看看Activity的onCreate(BundlesaveInstance)方法
protectedvoidonCreate(BundlesavedInstanceState){
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){
Parcelablep=savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p,mLastNonConfigurationInstances!=null
?mLastNonConfigurationInstances.fragments:null);
}
mFragments.dispatchCreate();
getApplication().dispatchActivityCreated(this,savedInstanceState);
mCalled=true;
}
由于我们的Fragment是由FragmentManager来管理,所以可以跟进FragmentManager.restoreAllState()方法,通过对当前活动的Fragmnet找到下面的代码块
for(inti=0;i<fms.mActive.length;i++){
FragmentStatefs=fms.mActive[i];
if(fs!=null){
Fragmentf=fs.instantiate(mActivity,mParent);
if(DEBUG)Log.v(TAG,"restoreAllState:active#"+i+":"+f);
mActive.add(f);
//Nowthatthefragmentisinstantiated(orcamefrombeing
//retainedabove),clearmInstanceincaseweendupre-restoring
//fromthisFragmentStateagain.
fs.mInstance=null;
}else{
mActive.add(null);
if(mAvailIndices==null){
mAvailIndices=newArrayList<Integer>();
}
if(DEBUG)Log.v(TAG,"restoreAllState:avail#"+i);
mAvailIndices.add(i);
}
}
接下来我们可以看看FragmentState.instantitate()方法的实现
publicFragmentinstantiate(Activityactivity,Fragmentparent){
if(mInstance!=null){
returnmInstance;
}
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,
"Instantiatedfragment"+mInstance);
returnmInstance;
}
可以看到最终转入到Fragment.instantitate()方法
publicstaticFragmentinstantiate(Contextcontext,Stringfname,Bundleargs){
try{
Class<?>clazz=sClassMap.get(fname);
if(clazz==null){
//Classnotfoundinthecache,seeifit'sreal,andtrytoaddit
clazz=context.getClassLoader().loadClass(fname);
sClassMap.put(fname,clazz);
}
Fragmentf=(Fragment)clazz.newInstance();
if(args!=null){
args.setClassLoader(f.getClass().getClassLoader());
f.mArguments=args;
}
returnf;
}catch(ClassNotFoundExceptione){
thrownewInstantiationException("Unabletoinstantiatefragment"+fname
+":makesureclassnameexists,ispublic,andhasan"
+"emptyconstructorthatispublic",e);
}catch(java.lang.InstantiationExceptione){
thrownewInstantiationException("Unabletoinstantiatefragment"+fname
+":makesureclassnameexists,ispublic,andhasan"
+"emptyconstructorthatispublic",e);
}catch(IllegalAccessExceptione){
thrownewInstantiationException("Unabletoinstantiatefragment"+fname
+":makesureclassnameexists,ispublic,andhasan"
+"emptyconstructorthatispublic",e);
}
通过此方法可以看到,最终会通过反射无参构造实例化一个新的Fragment,并且给mArgments初始化为原先的值,而原来的Fragment实例的数据都丢失了,并重新进行了初始化
通过上面的分析,我们可以知道Activity重新创建时,会重新构建它所管理的Fragment,原先的Fragment的字段值将会全部丢失,但是通过Fragment.setArguments(Bundlebundle)方法设置的bundle会保留下来。所以尽量使用Fragment.setArguments(Bundlebundle)方式来传递参数
以上所述是小编给大家介绍的Android中为什么要用Fragment.setArguments(Bundlebundle)来传递参数,希望对大家有所帮助,如果大家有任何疑问欢迎给我给我留言,小编会及时回复大家的!