Backstack上的Android片段占用了太多的内存

问题:

我有一个Android应用程序,允许用户浏览用户的配置文件ViewProfileFragment。在ViewProfileFragment中,用户可以点击将他带到各种用户照片的StoryViewFragment的图像。可以点击一个用户个人资料照片,将其带到新用户个人资料的另一个ViewProfileFragment实例。如果用户反复点击用户的个人资料,点击一个图像将其带到画廊,然后点击另一个配置文件,Fragments将迅速堆叠在内存中,导致可怕的OutOfMemoryError。以下是我正在描述的图表流程:

UserA点击Bob的个人资料。在Bob的个人资料中,UserA点击ImageA,将他带到各种用户(包括Bob)的照片库。 UserA点击Sue的配置文件,然后在她的一个图像中 – 处理重复等。

UserA -> ViewProfileFragment
         StoryViewFragment -> ViewProfileFragment
                               StoryViewFragment -> ViewProfileFragment

因此,从典型的流程可以看出,ViewProfileFragment和StoryViewFragment在后台堆叠的实例很多。

相关代码

我正在加载这些作为片段与以下逻辑:

//from MainActivity
fm = getSupportFragmentManager();
ft = fm.beginTransaction();
ft.replace(R.id.activity_main_content_fragment, fragment, title);
ft.addToBackStack(title);

我是什么

1)我专门使用FragmentTransaction替换,以便在替换发生时触发onPause方法。在内部onPause我试图释放尽可能多的资源(例如清除ListView适配器中的数据,“归零”变量等),以便当片段不是活动片段并推到后台时,将会更多的记忆释放了。但我努力释放资源只是部分成功。根据MAT我仍然有很多内存被GalleryFragment和ViewProfileFragment消耗。

2)我也删除了addToBackStack()的调用,但显然这提供了一个糟糕的用户体验,因为它们无法遍历(应用程序只是在用户点击后退按钮时关闭)。

3)我已经使用MAT找到所有的对象,我占用了很多空间,并且在onPause(和onResume)方法中以各种方式处理这些对象以释放资源,但是它们的大小仍然相当大。

4)我还在两个片段“onPause”中写了一个for循环,它使用以下逻辑将我的所有ImageViews设置为null:

 for (int i=shell.getHeaderViewCount(); i<shell.getCount(); i++) {

     View h = shell.getChildAt(i);
     ImageView v = (ImageView) h.findViewById(R.id.galleryImage);
       if (v != null) {
           v.setImageBitmap(null);
       }
  }

myListViewAdapter.clear()

质询

1)我是否忽略了一个方法来允许片段保留在后台,但也释放其资源,以便.replace(片段)的循环不会占用我的所有记忆?

2)当预计很多片段可以加载到后台时,什么是“最佳实践”?开发人员如何正确地处理这种情况? (或者我的应用程序中的逻辑本质上是有缺陷的,我只是做错了吗?)

任何帮助集思广益的解决方案将不胜感激。

事实证明,片段与父活动共享相同的生命周期。根据Fragment documentation

A fragment must always be embedded in an activity and the fragment’s
lifecycle is directly affected by the host activity’s lifecycle. For
example, when the activity is paused, so are all fragments in it, and
when the activity is destroyed, so are all fragments. However, while
an activity is running (it is in the resumed lifecycle state), you can
manipulate each fragment independently.

因此,除非父活动暂停,否则您无法触发片段的onPause()中清理某些资源所需的步骤。如果您有多个片段被父活动加载,那么很可能您正在使用某种机制来切换哪个片段是活动的。

您可能无法依靠onPause来解决您的问题,而是通过覆盖该片段上的setUserVisibleHint。这样,您可以在片段进入和退出视图时(例如,当您具有从FragmentA切换到FragmentB的PagerAdapter)确定在哪里设置资源或清理资源的好地方。

public class MyFragment extends Fragment {
  @Override
  public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
      //you are visible to user now - so set whatever you need 
      initResources();
    }
    else { 
     //you are no longer visible to the user so cleanup whatever you need
     cleanupResources();
    }
  }
}

正如已经提到的那样,您将物品堆叠在后台上,所以预计至少会有一点点内存占用,但是当使用上述技术的片段出现视图时,可以通过清理资源来最小化占用空间。

另一个建议是要真正了解内存分析器工具(MAT)的输出和内存分析。 Here is a good starting point.在Android中泄漏内存真的很容易,所以在我看来,熟悉这个概念,以及内存如何摆脱你的需要是必要的。可能的是,您的问题是由于您不会在片断出现时释放资源以及某种内存泄漏,因此如果您使用setUserVisibleHint触发清理资源的路线,正在使用的内存容量,那么内存泄漏可能是罪魁祸首,所以确保统治它们。

http://stackoverflow.com/questions/28483600/android-fragments-on-backstack-taking-up-too-much-memory

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:Backstack上的Android片段占用了太多的内存