片段 - getArguments() returns 一个空包

Fragment - getArguments() returns an empty bundle

希望有人能帮助我理解这一点:

  1. 我正在使用单个 activity 应用程序和许多在同一个容器中替换的片段,我正在使用 "Don't keep activities"选项启用

  2. 添加新片段时(在该片段中使用 <a href="https://developer.android.com/reference/android/app/FragmentTransaction.html#replace(int,%20android.app.Fragment,%20java.lang.String)" rel="noreferrer">FragmentTransaction replace()</a> method), I am using the <a href="https://developer.android.com/reference/android/app/Fragment.html#setArguments(android.os.Bundle)" rel="noreferrer">setArguments()</a> method to pass information to the new Fragment. It works as expected and I can get that information with <a href="https://developer.android.com/reference/android/app/Fragment.html#getArguments()" rel="noreferrer">getArguments()</a>。到目前为止一切正常...

  3. 在此之后,我将我的应用程序发送到后台。我看到堆栈中的所有片段都被销毁了,再次如预期

  4. 我把我的应用程序带到前台,在 getArguments() 方法中我得到一个空的 Bundle (不是空的,只是一个空的对象)而不是那个我在#2

  5. 中使用的数据

根据 Android 文档,setArguments() 中提供的参数将在片段销毁和创建过程中保留...所以,我的问题是:

  1. "will be retained across fragment destroy and creation"是否包括我描述的场景?

  2. 如果启用 "Don't keep activities" 选项是否会与 getArguments()/setArguments() 混淆?

  3. 除了 "Don't keep activities" 选项之外,还有其他方法可以测试正确的片段 creation/destroy 吗?

  4. 正确保留片段参数的更好方法是什么"alive"?我可以将它们保存在 onSaveInstanceState() 方法中,但想知道除此之外是否还有更多选项。

据我所知,不保留活动选项用于在内存不足的情况下测试您的应用程序,当其他资源无法为您的应用程序提供服务时,Android OS 终止了您的应用程序。 那时你有机会使用 onSavedInstanceState 保存数据,在片段中你可以使用 setRetainState(true) 根据我保存包数据的经验,使用 sharedPreferences。 SharedPreference 永远不会被销毁,直到您从设置中卸载或清除应用程序数据或通过 SharedPreference Editor 以编程方式将其删除。 有一件事很清楚,如果 activity 被销毁,那么它的所有碎片肯定会被销毁。 希望你明白我的意思。

我想问题是每次调用 activity 的 onCreate 时都会创建一个新的片段实例。假设您当前的代码如下所示:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ...
    ...
    fragment = SampleFragment.newInstance("sample");
    getSupportFragmentManager()
            .beginTransaction()
            .replace(R.id.container, fragment, "sample_fragment_tag")
            .commit();
    ...
}

因此,每次重新创建 activity 时,都会创建一个新的片段实例并将其附加到 activity。您应该避免这种情况,并且仅当 savedInstanceStatenull 时才创建片段的新实例,这意味着您的 activity 刚刚创建。否则,保存的片段实例将连同其参数以及 activity instance:

一起恢复
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ...
    ...
    if (savedInstanceState == null) {
        fragment = SampleFragment.newInstance("sample");
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.container, fragment, "sample_fragment_tag")
                .commit();
    } else {
        fragment = (SampleFragment) getSupportFragmentManager().findFragmentByTag("sample_fragment_tag");
    }
    ...
}

希望,这就是您要找的。

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/*Created by Foolish_Guy on 4/29/2017.*/                 

public class TestFragment extends Fragment {
private static final String USER_ID = "param1";
private static final String ARG_PARAM2 = "param2";

String userID;
String mParam2;

public static TestFragment newInstance(String param1, String param2) {
    TestFragment fragment = new TestFragment();
    Bundle args = new Bundle();
    Log.e("Data :", String.valueOf(param1));
    args.putString(USER_ID, param1);
    args.putString(ARG_PARAM2, param2);
    fragment.setArguments(args);
    return fragment;
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    super.onCreate(savedInstanceState);
    if (getArguments() != null) {
        userID = getArguments().getString(USER_ID);
        mParam2 = getArguments().getString(ARG_PARAM2);
    }
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_layout, container, false);
    return view;
}

}

初始化片段时您需要提供所需的参数。 您正在通过调用

对其进行初始化
TestFragment frag = TestFragment.newInstance("UID", "TEXT");

调用后将设置 Bundle 参数

onCreate (Bundle savedInstance) 中的下一个将被调用, 现在 Bundle 不会是空的了。

onSaveInstanceState 是应用被销毁但仍在最近应用列表中时保存数据的最佳位置。您节省的钱刚好足以重建您的应用程序在发送到后台时的外观,这就是用户离开您的应用程序的地方。用户将应用程序置于后台两三个星期,然后将其带到前台并微笑它记得它在哪里。

在首选项中,保存状态将使您的应用程序的用户将他们的 phone 扔到墙上。他们将从最近的应用程序中清除该应用程序,然后重新启动,然后永久卸载您的应用程序,因为它不是从一开始就从最近的应用程序中删除的,他们希望该应用程序从一开始就启动。我失去你了吗?

Activity和碎片化游戏是我们团队特别关注的点之一。所以这是我们想到的一些项目符号。

  1. "will be retained across fragment destroy and creation"是否包括我描述的场景?

请记住,当您进入后台时,"Don't keep Activities" 正在摧毁您的 Activity。稍后系统将尝试恢复您的最后状态,自动重新创建最后一个 Activity 及其片段。 Android 将保存必要的信息以恢复其最后状态。所以是的,应该涵盖您的场景。

  1. 如果启用 "Don't keep activities" 选项,它是否会与 getArguments()/setArguments() 混淆?

根据您的流程,您可以尝试一些问题。当 activity 在 onCreate 方法上重新创建 savedInstanceState 时将不为空。您应该使用此信息来避免重新创建或重新附加您的片段。系统会尝试为你恢复它,这就是片段不能有任何构造函数的原因。

  1. 除了 "Don't keep activities" 选项之外,还有其他方法可以测试正确的片段 creation/destroy 吗?

使用 FragmentTransaction。

1 - 使用 FragmentTransaction 将片段添加到 activity 而无需将其添加到后台堆栈。

2 - 使用 FragmentTransaction 将前一个片段替换为其他片段(或者可能是前一个片段的新实例)。当一个片段被其他片段替换并且它没有被添加到返回堆栈时 android 销毁它。

  1. 正确保留片段参数的更好方法是什么 "alive"?我可以将它们保存在 onSaveInstanceState() 方法中,但想知道除此之外是否还有更多选项。

可能您不需要在代码中保留参数包。 Android 会为您完成。但在 onAttach 方法(当片段将在屏幕上可用时调用的第一个方法)中恢复包数据并将它们存储为 class 属性供以后使用是一个很好的做法。

setRetainInstance(true);

在fragment的onCreate方法中。在通过标记从 SupportFragmentManager 添加或替换片段之前,如果片段值为空,则添加或替换为新片段。喜欢

Fragment fragment = fragmentManager.findFragmentByTag(tag);
if(fragment==null){
 fragmentManager.beginTransaction().replace(...
}

感谢大家的回答。我仍然不知道为什么我会遇到这个问题。我创建了一个示例项目来仅测试片段事务和 post 它在这里作为 @Reyansh 和 @Björn 询问。这是一个非常简单的项目 - 你猜怎么着 - 我无法重现该项目中的问题:每次重新创建 activity 时,getArguments() 方法都会提供相同的 Bundle。所以,一定是我项目中的其他原因导致了这种奇怪的行为。

我决定将@jDur 的答案标记为正确答案,因为它很好地解释了我的问题。