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)来传递参数,希望对大家有所帮助,如果大家有任何疑问欢迎给我给我留言,小编会及时回复大家的!