内存耗尽后ListView触发NullPointerException问题求助
这个问题我之前也碰到过,本质是系统内存回收后Activity重建时,生命周期回调顺序变化导致的:
正常启动应用时,你的Activity会先执行setContentView,完成a_images、a_emotes这些ListView实例的初始化,之后才会附加Fragment并触发onSectionAttached方法,所以一切正常。但当系统因内存不足回收你的Activity后,再次重建时,系统会先恢复Fragment的状态,触发Fragment的onAttach回调(进而调用你的onSectionAttached),而此时你的Activity可能还没完成setContentView和ListView的初始化,导致a_images还是null,调用它的方法就会抛出NullPointerException。
解决方案
1. 立即生效:添加Null检查(防御性编程)
这是最快修复崩溃的方式,在调用ListView的任何方法前,先判断它是否为null:
public void onSectionAttached(int number) { switch (number) { case 1: status = 1; mTitle = getString(R.string.title_section1); // 先检查ListView是否已初始化完成 if (a_images != null) { a_images.invalidateViews(); a_images.refreshDrawableState(); } break; // 处理其他case逻辑... } }
2. 更优雅:延迟操作到视图完全初始化后
你可以把ListView的刷新逻辑延迟到Activity视图完全初始化之后,比如在onPostCreate方法中处理(onPostCreate会在onCreate和所有视图初始化完成后调用):
@Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // 根据保存的状态执行对应的ListView刷新 if (status == 1 && a_images != null) { a_images.invalidateViews(); a_images.refreshDrawableState(); } }
同时修改onSectionAttached,只保存状态不直接操作ListView:
public void onSectionAttached(int number) { switch (number) { case 1: status = 1; mTitle = getString(R.string.title_section1); break; // 其他case逻辑... } }
3. 最佳实践:让Fragment管理自己的视图(如果适用)
如果你的ListView是Fragment布局的一部分,建议把ListView的初始化和操作逻辑移到Fragment内部,这样Fragment可以自己控制视图生命周期,避免跨组件的空指针问题:
public class PlaceholderFragment extends Fragment { private ListView a_images; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.your_fragment_layout, container, false); // 在Fragment内部初始化ListView a_images = rootView.findViewById(R.id.ascii_images); // 初始化适配器等逻辑 return rootView; } @Override public void onAttach(Context context) { super.onAttach(context); // 仅通知Activity更新标题,ListView操作留在Fragment内部 ((MainActivity) context).onSectionAttached(1); } // 在Fragment的合适生命周期中刷新ListView @Override public void onResume() { super.onResume(); if (a_images != null) { a_images.invalidateViews(); a_images.refreshDrawableState(); } } }
额外提示
别忘了在Activity中保存关键状态,内存回收后重建时可以恢复之前的状态:
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("status", status); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化ListView a_images = (ListView) findViewById(R.id.ascii_images); a_emotes = (ListView) findViewById(R.id.ascii_emotes); // 恢复之前保存的状态 if (savedInstanceState != null) { status = savedInstanceState.getInt("status"); } }
内容的提问来源于stack exchange,提问作者Jonáš Tichý

