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

本文共 6422 字,大约阅读时间需要 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 的保存和恢复过程。

    @Override
    protected 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/

    你可能感兴趣的文章
    Node.js安装与配置指南:轻松启航您的JavaScript服务器之旅
    查看>>
    Node.js安装及环境配置之Windows篇
    查看>>
    Node.js安装和入门 - 2行代码让你能够启动一个Server
    查看>>
    node.js安装方法
    查看>>
    Node.js官网无法正常访问时安装NodeJS的方法
    查看>>
    node.js模块、包
    查看>>
    node.js的express框架用法(一)
    查看>>
    Node.js的交互式解释器(REPL)
    查看>>
    Node.js的循环与异步问题
    查看>>
    Node.js高级编程:用Javascript构建可伸缩应用(1)1.1 介绍和安装-安装Node
    查看>>
    nodejs + socket.io 同时使用http 和 https
    查看>>
    NodeJS @kubernetes/client-node连接到kubernetes集群的方法
    查看>>
    NodeJS API简介
    查看>>
    Nodejs express 获取url参数,post参数的三种方式
    查看>>
    nodejs http小爬虫
    查看>>
    nodejs libararies
    查看>>
    nodejs npm常用命令
    查看>>
    nodejs npm常用命令
    查看>>
    Nodejs process.nextTick() 使用详解
    查看>>
    NodeJS yarn 或 npm如何切换淘宝或国外镜像源
    查看>>