如何正确删除保留的实例片段

时间:2013-03-22 07:59:25

标签: android

目前,我希望在配置更改期间保留昂贵的数据结构。我选择不使用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片段(称为 UIFragment )将从RetainInstanceFragment获取昂贵的数据结构。每当UIFragment上的配置发生变化时,UIFragment将始终尝试从RetainInstanceFragment获取“缓存”FragmentManager,然后才决定创建新的RetainInstanceFragment

示例代码如下。

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)

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

2 个答案:

答案 0 :(得分:7)

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

UIFragment

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

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

MyFragmentActivity

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片段仍会看到   保留片段实例仍然可用,因此不会创建新的   一。您以前的方法不符合片段的精神   片段不知道其他片段和片段   活动管理所有这些,因为它更了解整体   申请状态。

答案 1 :(得分:0)

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

            if (mWorkFragment != null) {
              fm.beginTransaction().remove(mWorkFragment).commitAllowingStateLoss();
            }