由于内存泄漏,我一直在重新编写一些代码。该代码是应用程序帮助部分的一部分,我们在其中使用 FragmentActivity 和 FragmentPageAdapter 允许用户滑动不同的帮助屏幕。每个 fragment ,下面的 SectionFragment 类,包括一个图像、一些标题文本和正文文本。
内存泄漏本身就是因为每次在 Fragment 中调用 onCreateView 时都会膨胀一个新 View 。
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 还是个新手,也不是最初编码工作的一部分,所以我不确定我的解决方案是否正确,但它类似于我在列表 Activity 中多次实现的 View 持有者模式。我非常感谢对以下实现的任何反馈,所以我会更放心这是正确的。
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, ViewGroup container,
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 内存分析器插件?它通常可以准确显示泄漏发生的位置。
我对 fragment 寻呼机内存泄漏/增加的第一个猜测是有一个动态适配器。 fragmentpageradapter 非常笨,不能很好地处理变化。
据我从源代码中所见,即使您将计数从 5 更改为 4,它也永远不会销毁 fragment (第五个 fragment 将保留在 fragment 管理器中)。这是因为它被分离而不是被摧毁。 fragmentstatepageradapter 会在 View 超出页边距时破坏 View 。
从长远来看,所有这些实际上并不会导致内存泄漏,因为当 fragmentmanager 稍后清除 fragment 时,这些 fragment 将被清除。但是,它会增加内存使用量(可能导致内存不足错误)。
每次创建 fragment 时都会创建一个新 View (如果将 retaininstance 设置为 true,则创建的次数会更多),但除非您使用 FragmentStatePagerAdapter,因为正常的寻呼机适配器会分离和附加,所以每个 fragment 应该只出现一次来自 fragment 管理器的相同 fragment 。在任何情况下, View 都将在 Activity 被销毁后被释放,除非您保留对它的引用。
您似乎确实在类里面保留了对 View 的引用,这是我通常尽量避免的事情。我更喜欢在需要时使用 getView().findViewById(),但在这种情况下,我认为成员引用应该由 gc 找到并删除。当然,这完全取决于您是否在其他地方泄漏了这些引用。
您不应该尝试在 fragment 中做类似 viewholder 的事情。在这个例子中它可能不会受到伤害,但我看不到你从中获得任何好处。只需在 oncreateview 中扩充 View 并设置值即可。
没有 View 的回收,例如在 ListView 中,因此 ViewHolder 模式没有意义,只会冒引入内存泄漏或由于悬空引用而延迟垃圾回收的风险。
View 不是为了重复使用而设计的,即使它们可能会像您使用的那样在某些父欺骗期间被重复使用。这只能作为最后的出路。然而,如果流程中有任何问题,它可能会导致可怕的泄漏。
如果有人可以合理使用它,请纠正我。
关于 ViewHolder 的旁注
我实际上会避免使用 viewholder,除非我真的需要它,即使在 ListView 中也是如此。我已经看到这些标签在某些游标适配器中导致非常严重的内存泄漏,我会在尝试引用保存之前考虑使我的行布局更简单。 Findviewbyid 在良好的布局中不会那么昂贵。
关于android - 仔细检查 fragment + view holder 模式是否正确实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9933783/
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife
我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden