Android 中为什么要用Fragment.setArguments(Bundle bundle)来传递参数
fragment在android3.0开始提供,并且在兼容包中也提供了fragment特性的支持。fragment的推出让我们编写和管理用户界面更快捷更方便了。
但当我们实例化自定义fragment时,为什么官方推荐fragment.setarguments(bundle bundle)这种方式来传递参数,而不推荐通过构造方法直接来传递参数呢?为了弄清这个问题,我们可以做一个测试,分别测试下这两种方式的不同
首先,我们来测试下通过构造方法传递参数的情况
public class framenttestactivity extends actionbaractivity { @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); if (savedinstancestate == null) { getsupportfragmentmanager().begintransaction() .add(r.id.container, new testfragment("param")).commit(); } } public static class testfragment extends fragment { private string marg = "non-param"; public testfragment() { log.i("info", "testfragment non-parameter constructor"); } public testfragment(string arg){ marg = arg; log.i("info", "testfragment construct with parameter"); } @override public view oncreateview(layoutinflater inflater, viewgroup container, bundle savedinstancestate) { view rootview = inflater.inflate(r.layout.fragment_main, container, false); textview tv = (textview) rootview.findviewbyid(r.id.tv); tv.settext(marg); return rootview; } } }
可以看到我们传递过来的数据正确的显示了,现在来考虑一个问题,如果设备配置参数发生变化,这里以横竖屏切换来说明问题,显示如下
发生了什么问题呢?我们传递的参数哪去了?为什么会显示默认值?不急着讨论这个问题,接下来我们来看看fragment.setarguments(bundle bundle)这种方式的运行情况
public class framenttest2activity extends actionbaractivity { @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout. activity_main); if (savedinstancestate == null) { getsupportfragmentmanager().begintransaction() .add(r.id. container, testfragment.newinstance("param")).commit(); } } public static class testfragment extends fragment { private static final string arg = "arg"; public testfragment() { log. i("info", "testfragment non-parameter constructor" ); } public static fragment newinstance(string arg){ testfragment fragment = new testfragment(); bundle bundle = new bundle(); bundle.putstring( arg, arg); fragment.setarguments(bundle); return fragment; } @override public view oncreateview(layoutinflater inflater, viewgroup container, bundle savedinstancestate) { view rootview = inflater.inflate(r.layout. fragment_main, container, false); textview tv = (textview) rootview.findviewbyid(r.id. tv); tv.settext(getarguments().getstring( arg)); return rootview; } } }
我们再来看看横竖屏切换后的运行情况
看到了吧,我们传递的参数在横竖屏切换的情况下完好保存了下来,正确的显示给用户
那么这到底是怎么回事呢,我们知道设备横竖屏切换的话,当前展示给用户的activity默认情况下会重新创建并展现给用户,那依附于activity的fragment会进行如何处理呢,我们可以通过源码来查看
先来看看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 ; }
由于我们的fragment是由fragmentmanager来管理,所以可以跟进fragmentmanager.restoreallstate()方法,通过对当前活动的fragmnet找到下面的代码块
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); } }
接下来我们可以看看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 ; }
可以看到最终转入到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)
方式来传递参数
以上所述是小编给大家介绍的android 中为什么要用fragment.setarguments(bundle bundle)来传递参数,希望对大家有所帮助