Подтвердить что ты не робот

Фрагменты не освобождаются из памяти

У меня есть активность, которая содержит View Pager с адаптером FragmentStatePagerAdapter. каждый раз, когда вы входите в действие, он занимает 200 мб памяти, после возврата из операции (финиш()), а затем повторного ввода, он будет добавлять и удваивать память, используемую на телефоне.

После устранения проблемы кажется, что менеджер фрагментов не освобождает фрагменты, хотя я пытаюсь их удалить, но просто не работает.

Я попытался опорожнить добавленный фрагмент, чтобы убедиться, что он не является внутренним внутри фрагмента, проблема остается.

мой код адаптера

   private class ChildrenPagerAdapter extends FragmentStatePagerAdapter
   {
      private List<ChildBean> childrenBean;

      public ChildrenPagerAdapter(FragmentManager fm, List<ChildBean> bean)
      {
         super(fm);
         this.childrenBean = bean;
      }

      @Override
      public int getItemPosition(Object object)
      {
         return PagerAdapter.POSITION_NONE;
      }

      @Override
      public Fragment getItem(int position)
      {

         ReportFragment reportFragment = new ReportFragment();
         reportFragment.childBean = childrenBean.get(position);
         reportFragment.position = position;
         reportFragment.mPager = mPager;
         if(position == 0)
         {
            reportFragment.mostLeft = true;
         }
         if(position == childrenNumber - 1)
         {
            reportFragment.mostRight = true;
         }

         return reportFragment;
      }

      @Override
      public int getCount()
      {
         return childrenNumber;
      }

      @Override
      public void destroyItem(ViewGroup container, int position, Object object)
      {
         // TODO Auto-generated method stub
         super.destroyItem(container, position, object);
      }
   }

мой код активности

    public class ReportActivity extends CustomActivity
{
   public ImageLoader imageLoader;
   private ViewPager mPager;
   private PagerAdapter mPagerAdapter;
   private int childrenNumber;
   private int currentChild;

   @Override
   protected void onDestroy()
   {
      mPager.removeAllViews();
      mPager.removeAllViewsInLayout();
      mPager.destroyDrawingCache();
      mPagerAdapter = null;
      mPager = null;
      System.gc();
      super.onDestroy();
   }

   @Override
   protected void onCreate(Bundle savedInstanceState)
   {

      super.onCreate(savedInstanceState);
      setCustomTitle(string.title_activity_reports);
      this.currentChild = getIntent().getIntExtra("itemselected", -1);

      getSupportFragmentManager().
   }

   @Override
   protected void onResume()
   {
      super.onResume();
      mPager = (ViewPager) findViewById(R.id.vpchildren);
      mPager.setOffscreenPageLimit(6);
      childrenNumber = MainActivity.bean.size();
      mPagerAdapter = new ChildrenPagerAdapter(getSupportFragmentManager(), MainActivity.bean);
      mPager.setAdapter(mPagerAdapter);
      mPager.setCurrentItem(currentChild);
   }
}

Код фрагмента:

public class ReportFragment extends Fragment
{

   public ChildBean childBean;
   public int position;
   public ImageView img;
   public ImageLoader imageLoader;
   public DisplayImageOptions options;
   private int pee = 0;
   private int poop = 0;
   private double sleep = 0.0;
   public ViewPager mPager;
   public boolean mostLeft = false;
   public boolean mostRight = false;

   public ReportFragment()
   {

   }

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

   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
   {
      ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.report_fragment, container, false);

      if(mostLeft)
      {
         rootView.findViewById(id.btnleft).setVisibility(View.GONE);
      }
      if(mostRight)
      {
         rootView.findViewById(id.btnright).setVisibility(View.GONE);
      }

      rootView.findViewById(id.btnleft).setOnClickListener(new OnClickListener()
      {

         @Override
         public void onClick(View v)
         {
            mPager.setCurrentItem(mPager.getCurrentItem() - 1);

         }
      });

      rootView.findViewById(id.btnright).setOnClickListener(new OnClickListener()
      {

         @Override
         public void onClick(View v)
         {
            mPager.setCurrentItem(mPager.getCurrentItem() + 1);

         }
      });

      SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy", Locale.ENGLISH);
      Date dobchild = new Date();

      ((TextView) rootView.findViewById(id.tvday)).setText(sdf.format(dobchild));

      ImageView childimg = (ImageView) rootView.findViewById(id.img_child);
      ((TextView) rootView.findViewById(id.tvchildname)).setText(childBean.childname);
      ((TextView) rootView.findViewById(id.tvclassname)).setText(((CustomApplication) getActivity().getApplication()).preferenceAccess.getCurrentClassName());

      Date dob = null;
      String age = "";
      try
      {
         dob = sdf.parse(childBean.childdob);
         age = GeneralUtils.getAge(dob.getTime(), getString(string.tv_day), getString(string.tv_month), getString(string.tv_year));
      }
      catch(ParseException e)
      {
         // TODO:
      }
      ((CustomTextView) rootView.findViewById(id.tvchildage)).setText(age);

      DisplayImageOptions options =
         new DisplayImageOptions.Builder().showImageForEmptyUri(drawable.noimage).showImageOnFail(drawable.noimage).showStubImage(drawable.noimage).cacheInMemory()
            .imageScaleType(ImageScaleType.NONE).build();

      imageLoader = ImageLoader.getInstance();
      imageLoader.displayImage(childBean.childphoto, childimg, options);
      final TextView tvpee = (TextView) rootView.findViewById(id.tvpeetime);
      final TextView tvpoop = (TextView) rootView.findViewById(id.tvpootimes);
      final TextView tvsleep = (TextView) rootView.findViewById(id.tvsleeptime);

      rootView.findViewById(id.btnaddpee).setOnClickListener(new OnClickListener()
      {
         @Override
         public void onClick(View v)
         {
            pee = pee + 1;
            if(pee > 9)
            {
               Toast.makeText(getActivity(), getString(string.tvareyousurepee), Toast.LENGTH_LONG).show();
            }
            tvpee.setText(String.format(getString(string.tvtimes), pee));
         }
      });

      rootView.findViewById(id.btnminuspee).setOnClickListener(new OnClickListener()
      {
         @Override
         public void onClick(View v)
         {
            if(pee > 0)
            {
               pee = pee - 1;
               tvpee.setText(String.format(getString(string.tvtimes), pee));
            }
         }
      });

      rootView.findViewById(id.btnpluspoo).setOnClickListener(new OnClickListener()
      {
         @Override
         public void onClick(View v)
         {
            poop = poop + 1;
            if(poop > 9)
            {
               Toast.makeText(getActivity(), getString(string.tvareyousurepoop), Toast.LENGTH_LONG).show();
            }
            tvpoop.setText(String.format(getString(string.tvtimes), poop));
         }
      });

      rootView.findViewById(id.btnminuspoo).setOnClickListener(new OnClickListener()
      {
         @Override
         public void onClick(View v)
         {
            if(poop > 0)
            {
               poop = poop - 1;
               tvpoop.setText(String.format(getString(string.tvtimes), poop));
            }
         }
      });

      rootView.findViewById(id.btnaddsleep).setOnClickListener(new OnClickListener()
      {
         @Override
         public void onClick(View v)
         {
            sleep = sleep + 0.25;
            tvsleep.setText(String.format(getString(string.tvhours), sleep));
         }
      });

      rootView.findViewById(id.btnminussleep).setOnClickListener(new OnClickListener()
      {
         @Override
         public void onClick(View v)
         {
            if(sleep > 0)
            {
               sleep = sleep - 0.25;
               tvsleep.setText(String.format(getString(string.tvhours), sleep));
            }
         }
      });

      rootView.findViewById(id.btnsave).setOnClickListener(new OnClickListener()
      {
         @Override
         public void onClick(View v)
         {
            Toast.makeText(getActivity(), "Report Saved.", Toast.LENGTH_LONG).show();
            getActivity().finish();
         }
      });

      return rootView;
   }
}

Пожалуйста, посоветуйте... Спасибо

4b9b3361

Ответ 1

Сам ViewPager имеет метод setOffscreenPageLimit, который позволяет указать количество страниц, поддерживаемых адаптером. Таким образом, ваши фрагменты, которые находятся далеко, будут уничтожены.

Прежде всего, глядя на ваш код, я не вижу, чтобы вы делали какие-либо действия по освобождению памяти в своих фрагментах onDestroy(). Тот факт, что сам фрагмент уничтожен и gc'ed не означает, что все выделенные вами ресурсы были удалены.

Например, моя большая проблема:

imageLoader = ImageLoader.getInstance();
imageLoader.displayImage(childBean.childphoto, childimg, options);

Из того, что я вижу здесь, кажется, что есть статический экземпляр ImageLoader, который вызывается каждый раз, когда появляется новый фрагмент, но я не вижу, где умирающий фрагмент попросит ImageLoader разгрузить его. Это выглядит подозрительно для меня.

Если бы я был вами, я бы сбросил файл HPROF моего приложения в тот момент, когда потребовалось дополнительно 200 мб (как вы утверждаете) после перезапуска активности и анализа ссылок через MAT (инструмент анализатора памяти). У вас явно есть проблемы с утечкой памяти, и я очень сомневаюсь, что проблема заключается в том, что сами фрагменты не уничтожаются.

Если вы не знаете, как анализировать кучу памяти, вот хороший видео. Я не могу подсчитать, сколько раз это помогало мне идентифицировать и избавляться от утечек памяти в моих приложениях.

Ответ 2

Не храните "сильные" ссылки на ViewPager или ImageView в своем фрагменте. Вы создаете циклическую ссылку, которая будет хранить все в памяти. Вместо этого, если вы должны сохранить ссылку на ViewPager или любой другой элемент, который ссылается на свой контекст вне вашей активности, попробуйте использовать WeakReference, например:

private WeakReference<ViewPager> mPagerRef; 
... 
mPagerRef = new WeakReference<ViewPager>(mPager);
...
final ViewPager pager = mPagerRef.get();

if (pager != null) {
    pager.setCurrentItem(...);
}

Следуя этому шаблону объектами, которые хранят ссылку на контекст Activity или Application (подсказка: любая ViewGroup, ImageView, Activity и т.д.), следует предотвращать "утечки памяти" в форме "циклов сохранения".

Ответ 4

После использования инструмента анализа памяти в eclipse я узнал, что то, что прилипает в моей памяти, является фактическим расположением моих фрагментов. Относительная компоновка в конкретном.

Причиной этого является CustomTextView, который я создал, который имеет собственный шрифт, заданный как шрифт.

 Typeface face=Typeface.createFromAsset(context.getAssets(), "Helvetica_Neue.ttf"); 
 this.setTypeface(face); 

Чтобы решить проблему утечки памяти, я просто сделал следующий ответ здесь:

public class FontCache {

    private static Hashtable<String, Typeface> fontCache = new Hashtable<String, Typeface>();

    public static Typeface get(String name, Context context) {
        Typeface tf = fontCache.get(name);
        if(tf == null) {
            try {
                tf = Typeface.createFromAsset(context.getAssets(), name);
            }
            catch (Exception e) {
                return null;
            }
            fontCache.put(name, tf);
        }
        return tf;
    }
}