博客
关于我
Android解惑 - 为什么要用Fragment.setArguments(Bundle bundle)来传递参数
阅读量:372 次
发布时间:2019-03-04

本文共 6317 字,大约阅读时间需要 21 分钟。

为什么官方推荐使用 Fragment.setArguments(Bundle bundle) 而不是构造方法传递参数?

在开发Android应用时,Fragment 作为一种轻量级的UI 组件,能够帮助我们更高效地管理和复用用户界面。然而,关于如何传递参数给自定义 Fragment,官方推荐使用 Fragment.setArguments(Bundle bundle) 而不是直接在构造方法中传递参数,这背后有着深刻的原因。以下是详细的分析和解释:

一、Fragment 的生命周期与状态保存

Fragment 的生命周期与 Activity 的生命周期紧密相关。每当 Activity 的配置发生变化(如屏幕旋转或切换 Activity),FragmentManager 会将当前显示的 Fragment 的状态进行保存,并在必要时重新恢复这些状态。这种状态保存机制是 Fragment 在 Android 中的核心特性之一。

  • onSaveInstanceState():在 Activity 的 onSaveInstanceState 方法中,FragmentManager 会将当前显示的 Fragment 的状态(包括 Fragment 的参数)保存到 Bundle 中。
  • onActivityCreated():当 Activity 的创建完成后,FragmentManager 会恢复保存的 Fragment 状态,包括参数,从而重新构建 UI。

二、传递参数的最佳做法

  • 通过构造方法传递参数的局限性

    如果使用构造方法直接传递参数,例如:

    public TestFragment(String arg) {    // 初始化逻辑}

    并在 Activity 中创建 Fragment 时:

    fragment = new TestFragment("参数");

    这种方法存在以下问题:

    • 状态不定性:在设备屏幕切换(如横竖屏转换)时,Fragment 的状态会被丢失,因为构造方法传递的参数不是通过 Fragment 的状态机制保存的。
    • 参数丢失:如果 Activity 被重新创建,Fragment 的参数不会自动恢复,导致参数丢失,UI 将无法正确显示。
  • Fragment.setArguments(Bundle bundle) 的优势

    官方推荐的 Fragment.setArguments(Bundle bundle) 方法提供了更优雅且更可靠的参数传递机制。通过创建一个 Bundle 对象,包含所需的参数,并将其传递给 Fragment,可以确保参数在 Fragment 的生命周期中得到正确保存和恢复。

    • 参数保存:Fragment 会将传递的 Bundle 对象包含在其自身的 savedInstance 中,这样即使设备发生配置变化,参数也能被正确保存和恢复。
    • 与生命周期兼容:通过 Fragment.setArguments(Bundle bundle) 方法,参数会被自动包含在 Fragment 的状态中,从而在 Activity 的 onActivityCreated 方法中被正确读取和应用。
  • 三、代码示例对比

  • 构造方法传递参数的示例

    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;    }}
    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();        }    }}
  • 使用 Fragment.setArguments(Bundle bundle) 的示例

    public static class TestFragment extends Fragment {    private static final String ARG = "arg";    private String mArg;    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;    }}
    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();        }    }}
  • 四、横竖屏切换后的表现对比

  • 构造方法传递参数的表现

    当设备发生横竖屏切换时,Activity 会重新创建,并恢复之前的 Fragment 状态。由于参数是通过构造方法传递的,Fragment 的状态不会自动包含参数,导致参数丢失,UI 将显示默认值。

  • Fragment.setArguments(Bundle bundle) 的表现

    由于参数通过 Fragment 的状态机制保存,横竖屏切换后,Fragment 会自动从 Bundle 中读取参数,确保 UI 正确显示传递的参数。

  • 五、技术细节解释

  • Activity 的 onCreate 方法

    Activity 的 onCreate 方法负责初始化组件,并恢复 Fragment 的状态。FragmentManager 会处理 Fragment 的保存和恢复过程。

    @Overrideprotected void onCreate(Bundle savedInstanceState) {    if (DEBUG_LIFECYCLE) Log.v(TAG, "onCreate " + this + ": " + savedInstanceState);    // 处理 Fragment 的恢复    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;}
  • FragmentManager 的 restoreAllState 方法

    restoreAllState 方法负责从 Bundle 中恢复 Fragment 的状态,确保参数和 Fragment 的配置被正确重建。

    for (int i = 0; i < fms.mactive.length; i++) {    fragmentstate fs = fms.mActive[i];    if (fs != "null") {        fragment f = fs.instantiate(mActivity, mParent);        Log.v(TAG, "restoreAllState: active #" + i + ": " + f);        mactive.add(f);        fs.minstance = "null"; // 清除可能重复恢复的状态    } else {        mactive.add(null);    }    mavailindices.add(i);}
  • FragmentState 的 instantiate 方法

    instantiate 方法负责根据 Fragment 的配置重新创建 Fragment 实例,并恢复其状态。

    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.setArguments(Bundle bundle) 的优势在于其与 Fragment 的状态保存机制完美结合,确保参数在设备配置变化时能够正确保存和恢复。而通过构造方法传递参数则不具备这种机制,容易导致参数丢失,影响 UI 的正确显示。因此,官方推荐的 Fragment.setArguments(Bundle bundle) 方法是更优的选择,能够更好地处理 Fragment 的状态和参数传递,确保应用在横竖屏切换等场景下的稳定性和一致性。

    转载地址:http://jtsr.baihongyu.com/

    你可能感兴趣的文章
    No resource identifier found for attribute 'srcCompat' in package的解决办法
    查看>>
    no session found for current thread
    查看>>
    No toolchains found in the NDK toolchains folder for ABI with prefix: mips64el-linux-android
    查看>>
    NO.23 ZenTaoPHP目录结构
    查看>>
    no1
    查看>>
    NO32 网络层次及OSI7层模型--TCP三次握手四次断开--子网划分
    查看>>
    NOAA(美国海洋和大气管理局)气象数据获取与POI点数据获取
    查看>>
    NoClassDefFoundError: org/springframework/boot/context/properties/ConfigurationBeanFactoryMetadata
    查看>>
    node exporter完整版
    查看>>
    Node JS: < 一> 初识Node JS
    查看>>
    Node Sass does not yet support your current environment: Windows 64-bit with Unsupported runtime(72)
    查看>>
    Node 裁切图片的方法
    查看>>
    Node+Express连接mysql实现增删改查
    查看>>
    node, nvm, npm,pnpm,以前简单的前端环境为什么越来越复杂
    查看>>
    Node-RED中Button按钮组件和TextInput文字输入组件的使用
    查看>>
    Node-RED中Switch开关和Dropdown选择组件的使用
    查看>>
    Node-RED中使用html节点爬取HTML网页资料之爬取Node-RED的最新版本
    查看>>
    Node-RED中使用JSON数据建立web网站
    查看>>
    Node-RED中使用json节点解析JSON数据
    查看>>
    Node-RED中使用node-random节点来实现随机数在折线图中显示
    查看>>