2013-08-14 3 views
20

У меня есть активность, которая содержит 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; 
    } 
} 

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

+1

Вы можете разместить код ReportFragment? – Henrique

+0

@Henrique сделано, спасибо –

+1

Действительно ли ваше приложение занимает 200 мб? «FragmentStatePagerAdapter» используется для обеспечения максимальной сохранности наименьшего количества фрагментов в памяти. В вашем приложении, однако, вы против этого, используя 'mPager.setOffscreenPageLimit (6);' в основном сохраняя в памяти максимум 13 фрагментов. – Luksprog

ответ

21

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

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

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

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

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

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

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

3

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

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

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

После этого шаблон с объектами, которые хранят ссылку на Контекст активности или приложения (подсказка: любая ViewGroup, ImageView, Activity и т. Д.) Должна предотвращать возникновение «утечек памяти» в форме «циклов сохранения».

+0

Можете ли вы привести пример, где это будет предпочтительнее? Для каждого представления в каждом фрагменте? –

1

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

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

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

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

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; 
    } 
} 
+1

И вы думаете, что неприемлемый ответ, который на самом деле дал вам правильные указания (MAT) и информация, подходит? – EvilDuck

+0

нет, вы правы, мой плохой, что не заметил, что он не принял ваш ответ –

Смежные вопросы