9

目前,我想在配置更改期间保留昂贵的数据结构。我选择不使用Bundle来处理它,因为昂贵的数据结构不可打包。

因此,我使用一个非 UI 片段(称为RetainInstanceFragment),用它setRetainInstance(true)来保存数据结构。

public class RetainInstanceFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Creating expensive data structure
        expensiveDataStructure = CreateExpensiveDataStructure();

        // Tell the framework to try to keep this fragment around
        // during a configuration change.
        setRetainInstance(true);
    }

    public ExpensiveDataStructure expensiveDataStructure = null;
}

UI Fragment(称为UIFragment)将从RetainInstanceFragment. 每当在 上发生配置更改时,UIFragment总是UIFragment会在决定创建新的.RetainInstanceFragmentFragmentManagerRetainInstanceFragment

示例代码如下。

public class UIFragment extends SherlockListFragment
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        FragmentManager fm = getFragmentManager();

        // Check to see if we have retained the worker fragment.
        retainInstanceFragment = (RetainInstanceFragment)fm.findFragmentByTag("data");

        // If not retained (or first time running), we need to create it.
        if (retainInstanceFragment == null) {
            retainInstanceFragment = new RetainInstanceFragment();
            fm.beginTransaction().add(watchlistArrayFragment, "data").commit();
        } else {
            // We can re-use retainInstanceFragment.expensiveDataStructure even
            // after configuration change.
        }
    }
}

但是,有一个问题。每当我销毁旧UIFragment的并用新的替换它时UIFragment我希望旧的RetainInstanceFragment也会被销毁。这是我如何破坏和创造新的UIFragment

public class MyFragmentActivity extends SlidingFragmentActivity    
    // Being triggered when there is different menu item in sliding menu being
    // selected.
    public void selectActiveContent(Country country) {
        Fragment fragment = new UIFragment(country);
        getSupportFragmentManager().beginTransaction().replace(R.id.content, fragment).commitAllowingStateLoss();
    }

但旧RetainInstanceFragment的永远不会被摧毁。

我的猜测是,也许我忘记在UIFragment. 因此,我添加以下代码

UIFragment

@Override
public void onDetach() {
    super.onDetach();
    // To differentiate whether this is a configuration changes, or we are
    // removing away this fragment?
    if (this.isRemoving()) {
        FragmentManager fm = getFragmentManager();
        fm.beginTransaction().remove(retainInstanceFragment).commit();
    }
}

但是,它并不总是有效。我执行了几次滑动菜单点击。

1. selectActiveContent() -> Create new UIFragment and new RetainInstanceFragment
2. selectActiveContent() -> Create new UIFragment, but re-use previous RetainInstanceFragment. (Wrong behavior)
3. selectActiveContent() -> Create new UIFragment, and new RetainInstanceFragment.
4. selectActiveContent() -> Create new UIFragment, but re-use previous RetainInstanceFragment. (Wrong behavior)

知道如何正确删除保留的实例片段吗?

4

2 回答 2

7

正如@Luksprog 所建议的,以下方法有效。但是,它仍然没有解释为什么之前的清理工作onDetach不起作用。如果有人能解释为什么这个解决方案有效而以前没有,我将非常感激。:)

UIFragment

@Override
public void onDetach() {
    super.onDetach();
}

public void cleanupRetainInstanceFragment() {
    FragmentManager fm = getFragmentManager();
    fm.beginTransaction().remove(this.retainInstanceFragment).commit();
}

我的片段活动

public class MyFragmentActivity extends SlidingFragmentActivity    
    // Being triggered when there is different menu item in sliding menu being
    // selected.
    public void selectActiveContent(Country country) {

        // *******************************************
        // Solution suggested by @Luksprog. It works!
        // But I have no idea why it works and previous doesn't work...
        // *******************************************
        Fragment oldFragment = getSupportFragmentManager().findFragmentById(R.id.content);
        if (oldFragment instanceof UIFragment) {
            ((UIFragment)oldFragment).cleanupRetainInstanceFragment();
        }

        Fragment fragment = new UIFragment(country);
        getSupportFragmentManager().beginTransaction().replace(R.id.content, fragment).commitAllowingStateLoss();
    }

(已编辑)@Luksprog 的有用评论

片段交易不会立即进行。我的假设是在 onDetach() 回调中执行该事务不会在 UI 片段的替换事务完成之前删除保留片段实例,因此您的新 UI 片段仍将看到保留片段实例仍然可用,因此它不会创建新的一。您之前的方法不符合 Fragments 框架的精神,其中 Fragment 不知道其他 Fragment,并且 Activity 管理所有这些 Fragment,因为它对整体应用程序状态了解更多。

于 2013-03-22T09:28:47.583 回答
0

我认为您可以从片段事务中删除片段。

            if (mWorkFragment != null) {
              fm.beginTransaction().remove(mWorkFragment).commitAllowingStateLoss();
            }
于 2015-05-20T07:38:02.967 回答