内存泄漏表现自己,因为每次在Fragment中调用onCreateView时,新视图都会膨胀.
public class SectionFragment extends Fragment { private ImageView imgvw; private TextView headerTxvw; private TextView bodyTxvw; public int[][] content; protected int pageIdx; public SectionFragment(int idx,int[][] content ){ super(); pageIdx = idx; this.content = content; } protected int getPageIdx() { return pageIdx; } protected Drawable getImageDrawable() { return getResources().getDrawable( content[pageIdx][0] ); } protected String getHeaderText() { return getResources().getString( content[pageIdx][1] ); } protected String getSubeaderText() { return getResources().getString( content[pageIdx][2] ); } protected void loadView( View vw ) { imgvw = (ImageView)vw.findViewById( R.id.top_img ); headerTxvw = (TextView)vw.findViewById( R.id.header_txt ); bodyTxvw = (TextView)vw.findViewById( R.id.body_txt ); imgvw.setImageDrawable( getImageDrawable() ); headerTxvw.setText( getHeaderText() ); bodyTxvw.setText( getSubeaderText() ); } @Override public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState) { View vw = inflater.inflate( R.layout.help_fragment,null ); loadView( vw ); return vw; } }
我还是Fragments的新手,并不是原始编码工作的一部分,所以我不确定我的解决方案是否正确,但它类似于我在List活动中多次实现的视图持有者模式.我真的很感激有关以下实施的任何反馈,所以我会更加放心,这是正确的.
public class SectionFragment extends Fragment { static private class ViewHolder { ImageView imgvw; TextView headerTxvw; TextView bodyTxvw; } public int[][] _content; protected int _pageIdx; public SectionFragment( int idx,int[][] content ){ super(); _pageIdx = idx; _content = content; } protected int getPageIdx() { return _pageIdx; } protected Drawable getImageDrawable() { return getResources().getDrawable( _content[_pageIdx][0] ); } protected String getHeaderText() { return getResources().getString( _content[_pageIdx][1] ); } protected String getSubeaderText() { return getResources().getString( _content[_pageIdx][2] ); } protected void loadView( View vw ) { _holder.imgvw.setImageDrawable( getImageDrawable() ); _holder.headerTxvw.setText( getHeaderText() ); _holder.bodyTxvw.setText( getSubeaderText() ); } private View _vw; private ViewHolder _holder; @Override public View onCreateView(LayoutInflater inflater,Bundle savedInstanceState) { if ( _vw == null ) { _vw = inflater.inflate( R.layout.help_fragment,null ); _holder = new ViewHolder(); _holder.imgvw = (ImageView)_vw.findViewById( R.id.top_img ); _holder.headerTxvw = (TextView)_vw.findViewById( R.id.header_txt ); _holder.bodyTxvw = (TextView)_vw.findViewById( R.id.body_txt ); _vw.setTag( _holder ); } else { ViewParent oldparent = (ViewParent)_vw.getParent(); if ( oldparent != container ) { ((ViewGroup)oldparent).removeView( _vw ); } _holder = (ViewHolder)_vw.getTag(); } loadView( _vw ); return _vw; } }
我没有包含与此代码相关的其他类,特别是FragmentActivity和FragmentPagerAdapter,因为它们似乎正确实现,但如果请求我也可以包含它们.
解决方法
你确定它是在泄漏内存而不仅仅是延迟垃圾收集吗?您是否在应用程序上运行eclipse内存分析器插件?它通常可以准确显示泄漏发生的位置.
片段viewpager及其回收(或缺乏)
我对片段寻呼机内存泄漏/增加的第一个猜测是拥有一个动态适配器. fragmentpageradapter非常愚蠢,无法很好地处理更改.
据我所知,从源头看它永远不会破坏碎片,即使你将数量从5改为4(第五个碎片将保留在碎片管理器中).这是因为它被分离而不是被破坏.但是,当它们位于页边距之外时,fragmentstatepageradapter将销毁它们.
从长远来看,所有这些都不会导致内存泄漏,因为当片段管理器稍后清除片段时,这些片段将被清除.然而,它可以相当多地增加内存使用(可能导致outofmemoryerror).
确实每次创建片段时都会创建一个新视图(如果将retaininstance设置为true,则通常会创建新视图),但除非您使用FragmentStatePagerAdapter,每个片段只应出现一次,因为正常的寻呼机适配器会分离并附加相同的片段来自片段管理器.在任何情况下,视图将在活动被销毁后处理,除非您保留对它的引用.
你似乎确实在你的课程中保留了对视图的引用,这是我通常试图避免的.我更喜欢在需要时使用getView().findViewById(),但在这种情况下,我认为gc会找到并删除成员引用.这一切都取决于你当然是否泄漏了那些参考文献.
在片段寻呼机中的观察者
您不应该尝试像片段中的视图符号那样执行某些操作.在这个例子中它可能不会受到伤害,但我看不到你从中获得任何东西.只需在oncreateview中膨胀视图并设置值并完成它.
列表视图中没有回收视图,因此ViewHolder模式没有任何意义,只会因为悬空引用而引入内存泄漏或延迟垃圾收集.
视图不会被重用,即使它们可以在您使用过的某些父级技巧中进行.这应该只是作为最后的出路.如果在流程中有任何关闭,它可能会导致可怕的泄漏.
如果有人能够合理使用它,请纠正我.
关于ViewHolder的旁注
我实际上不会使用viewholder,除非我真的需要它甚至在listview中.我已经看到这些标签导致真正令人讨厌的内存泄漏,例如一些游标适配器,我会在尝试引用保存之前考虑使我的行布局更简单. Findviewbyid在良好的布局中不会那么昂贵.