欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 中为什么要用Fragment.setArguments(Bundle bundle)来传递参数

程序员文章站 2024-02-20 10:50:04
fragment在android3.0开始提供,并且在兼容包中也提供了fragment特性的支持。fragment的推出让我们编写和管理用户界面更快捷更方便了。 但当我们...

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)来传递参数,希望对大家有所帮助