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)来传递参数(转)的更多相关文章

  1. Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle) instead

    “Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(B ...

  2. Error:Error: Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle) instead [ValidFragment]

    原文博客链接:https://blog.csdn.net/chniccs/article/details/51258972 在创建fragment时,你可能在打包时碰到如下错误 Error:Error ...

  3. “Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle)instead”

    “Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(B ...

  4. Android典型界面设计——ViewPage+Fragment实现区域顶部tab滑动切换

    一.问题描写叙述 本系列将结合案例应用,陆续向大家介绍一些Android典型界面的设计,首先说说tab导航,导航分为一层和两层(底部区块+区域内头部导航).主要实现方案有RadioGroup+View ...

  5. android fragment传递参数_fragment之间传值的两种方法

    在Activity中加载Fragment的时候.有时候要使用多个Fragment切换.并传值到另外一个Fragment.也就是说两个Fragment之间进行参数的传递.查了很多资料.找到两种方法.一种 ...

  6. Android常用控件之Fragment仿Android4.0设置界面

    Fragment是Android3.0新增的概念,是碎片的意思,它和Activity很相像,用来在一个Activity中描述一些行为或部分用户界面:使用多个Fragment可以在一个单独的Activi ...

  7. 【Android 应用程序开发】 Fragment 详细说明

    笔者 : 汉书亮 转载请著名出处 : http://blog.csdn.net/shulianghan/article/details/38064191 本博客代码地址 : -- 单一 Fragmen ...

  8. [Android Pro] java.lang.IllegalStateException: Fragment(XXFragment) not attached to Activity异常

    转载:http://blog.csdn.net/winson_jason/article/details/20357435 下边两个问题,是在开发中碰到的一些关于Fragment的偶发性的问题,今天时 ...

  9. 如何向一个Fragment传递参数---setArguments方法的介绍

    在我们平常开发中经常会用到Fragment,当我们使用Fragment时一般是通过new Fragment的构造方法来实现,如果我问你怎么向一个Fragment传递参数,你是不是会首先想到通过构造方法 ...

随机推荐

  1. POJ 1001 Exponentiation(JAVA,BigDecimal->String)

    题目 计算实数a的n次方,具体输出格式看案例 import java.util.*; import java.math.*; public class Main { public static voi ...

  2. cf div2 239 D

    D. Long Path time limit per test 1 second memory limit per test 256 megabytes input standard input o ...

  3. 最常用的javascript方法函数

    字符串长度截取 function cutstr(str, len) { var temp, icount = 0, patrn = /[^\x00-\xff]/, strre = "&quo ...

  4. .net web程序发布之后,出现编译错误

    .net web程序发布之后,在IIS上浏览的时候出现编译错误. CS0016: 未能写入输出文件“c:\Windows\Microsoft.NET\Framework\v4.0.30319\Temp ...

  5. C Primer Plus之指针

    c之精髓——指针(pointer)——用来存储地址的变量.一般来讲,指针是一个其数值为地址的变量(或更一般地说是一个数据对象). 一元运算符&可以取得变量的存储地址,一个变量的地址可以被看作是 ...

  6. C 和C++ 名称修饰规则

    C名称修饰规则 1.对于使用__cdecl调用约定的函数,在函数名称前加一下划线,不考虑参数和返回值. 2.对于使用__fastcall调用约定的函数,在函数名称前后各加一@符号,后跟参数的长度,不考 ...

  7. java异常——RuntimeException和User Define Exception

    1.RuntimeException public class RuntimeException { public static void main(String[] args) { // TODO ...

  8. 关于MySQL

    DBMS - 数据库管理系统(Database Management System) RDBMS - 关系数据库管理系统(Relational Database Management System) ...

  9. istream, outstream使用及常见错误

    使用方法: 使用filebuf打开文件,并拷贝给istream/ostream. 如下面的例子中,实现读取并处理deseq文件夹下所有文件,输出到ostream fw. code: #include& ...

  10. SPOJ 416 Divisibility by 15 细节题

    一个结论:一个数,如果它的所有数字之和能被3整除,那么这个数也能被3整除. 最后一位肯定是0或者5,如果没有就impossible. 剩下的就是,如何删除尽量少的数,使所有数字之和为3的倍数. 情况比 ...