内存问题 - 碎片化

4
我最近重构了一个应用程序,将 ViewFlipper 替换为 FrameLayout,并在其中交换 Fragment。
每当用户请求其中一种视图时:
    public void showLibraryOf(long publisherId) {
        library = new DownloadLibraryFragment(id, viewFactory());

        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.container, library);
        ft.commit();

        library.setAdapterObserver(this);
    }

    public void showMyLibraryOf(long publisherId) {
        myLibrary = new MyLibraryFragment(id, viewFactory());

        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.container, myLibrary);
        ft.commit();
    }

    public void showHelp() {
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.container, new HelpFragment());
        ft.commit();
    }

我创建了一个新的Fragment并替换了旧的。那些从屏幕上移除的Fragment会调用onDestroy方法,但我加载到屏幕上的位图所占用的内存并没有被释放,因此在Fragment之间进行一些交换后应用程序会崩溃。
我也尝试在onDestroy中删除引用。
@Override
public void onDestroyView() {
    destroy();
    super.onDestroyView();   
    adapter.clear();
    adapter.clearObservers();
    adapter.notifyDataSetChanged();

    view.setAdapter(new ArrayAdapter<Journal>(getActivity(), 0));

    adapter = null;
    view = null;
}

但是内存一直在增长。

有人知道任何解决方案吗?也许可以重用片段?有效地销毁它?我在听着。


看起来有人在这里遇到了类似的问题:https://dev59.com/MmDVa4cB1Zd3GeqPcl2- 这对解决问题有帮助吗? - scriptocalypse
Bitmap#recycle() 如果可以的话,或者使用 System.gc()。仅清除适配器不会立即回收位图。 - zapl
scriptocalypse,这很相似,但是通过Pager实现,因此生命周期由FragmentAdapter管理。这对我没有帮助。 - Marcos Vasconcelos
在通常的视图中,将其从父级中移除后,zapl 真正释放了它的内存。 - Marcos Vasconcelos
好吧,至少使用图像缓存应用程序不会再次加载所有内容。在使用了55Mb的内存后,它们会释放出来供新的使用。 - Marcos Vasconcelos
1个回答

6
我忘记了原始来自Stack Overflow的问题,但似乎有一种方法可以很好地工作,那就是覆盖FragmentActivity的onAttachFragment方法,然后将每个传入的片段存储为WeakReference。然后,不要使用FragmentTransaction的replace方法,而是回收所有相关的片段(适用于此情况)。
以下是一个FragmentActivity的示例,其中包含额外的成员和方法,它在onCreate中创建默认片段,并通过onNewIntent响应更改:
private List<WeakReference<Fragment>> mFragments = 
    new ArrayList<WeakReference<Fragment>>();

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.add(R.id.fragment_container, MyFragment.newInstance("default"));
    ft.commit();
}

@Override
protected void onNewIntent(Intent intent) {
    setIntent(intent);
    String section = intent.getStringExtra("section");
    recycleFragments();
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.add(R.id.fragment_container, MyFragment.newInstance(section));
    ft.commit();
}

@Override
public void onAttachFragment(Fragment fragment) {
    mFragments.add(new WeakReference<Fragment>(fragment));
}

private void recycleFragments() {
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();

    for (WeakReference<Fragment> ref : mFragments) {
        Fragment fragment = ref.get();
        if (fragment != null) {
            ft.remove(fragment);
        }
    }

    ft.commit();
}

现在,如果您监视堆,您应该注意到它的大小没有增加。当您有包含位图的嵌套片段,这些位图由于某种原因似乎无法正确回收时,此解决方案通常会发挥作用。我希望有更优雅的解决方案,但这个解决方案有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接