美文网首页android开发Android功能
为什么要通过Fragment.setArguments(Bund

为什么要通过Fragment.setArguments(Bund

作者: lxbnjupt | 来源:发表于2018-04-11 15:56 被阅读0次

    Fragment在开发中是经常使用的,我们在创建一个Fragment对象实例的时候一般都会通过new Fragment()构造方法来实现。如果在创建Fragment的时候需要向其传递数据,则可以通过构造方法直接来传递参数,或者通过Fragment.setArguments(Bundle bundle)这种方式来传递参数。这两种参数传递方式大概如下:

    方式一:通过构造方法传递参数

    在创建Fragment的时候,使用 MyFragment fragment = new MyFragment(parameter) 来传递参数。

    public class MyFragment extends Fragment {
        public MyFragment(Parameter p){
            //将参数保存起来
        }
    }
    

    方式二:通过Fragment.setArguments(Bundle)传递参数

    在创建Fragment的时候,使用MyFragment fragment = MyFragment.newInstance(paramter) 来传递参数。

    public class MyFragment extends Fragment {
        public static MyFragment newInstance(Parameter p) {
            MyFragment myFragment = new MyFragment();
            Bundle args = new Bundle();
            args.putInt("someParameter", p);
            myFragment.setArguments(args);
            return myFragment;
        }
    }
    

    对比分析

    看上去这两种方式没有什么本质的区别,但是通过构造方法传递参数的方式是有隐患的。根据Android文档说明,当一个Fragment重新创建的时候,系统会再次调用Fragment中的默认构造函数,注意是默认构造函数。即,当你创建了一个带有参数的Fragment的之后,一旦由于什么原因(例如横竖屏切换)导致你的Fragment重新创建。那么,很遗憾,你之前传递的参数都不见了,因为recreate你的Fragment的时候,调用的是默认构造函数。因此,官方推荐使用Fragment.setArguments(Bundle bundle)这种方式来传递参数,而不推荐通过构造方法直接来传递参数。

    推荐使用Fragment.setArguments(Bundle)传递参数原因

    (1)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 ;  
    }  
    

    (2)跟进FragmentManager.restoreAllState()方法

      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);  
               }  
    } 
    

    (3)跟进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 ;  
        } 
    

    (4)最终转入到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)方式来进行参数传递。

    注意:setArguments方法的调用必须要在Fragment与Activity关联之前,即setArgument方法的使用必须要在FragmentTransaction 的commit之前使用。

    相关文章

      网友评论

        本文标题:为什么要通过Fragment.setArguments(Bund

        本文链接:https://www.haomeiwen.com/subject/jsaahftx.html