Android SharedPreferences空指针异常求助:偶发崩溃如何修复?
看起来你遇到的是Fragment中常见的Context为空导致的偶发崩溃问题,我来帮你拆解下原因和可行的解决办法:
问题根源
从错误栈可以看出,NPE出现在PrefManager的构造函数里调用_context.getSharedPreferences()时,而_context是从PlayerFragment的getActivity()传过来的。偶发的核心原因是:
当Fragment已经和宿主Activity解除关联时(比如Fragment被destroy、detach,或者Activity正在销毁过程中),
getActivity()会返回null。
具体到你的场景:ExoPlayer的onPlayerStateChanged是异步回调,这个回调可能在Fragment已经不在前台、甚至已经和Activity分离的时候触发(比如用户快速切换Fragment、切到后台),此时调用addView(),里面的new PrefManager(getActivity())就会传入null,最终导致调用getSharedPreferences时抛出NPE。
解决思路
1. 在使用getActivity()前先判空
在PlayerFragment的addView()方法里,先检查Context是否有效,避免传入null到PrefManager:
public void addView() { Context context = getActivity(); if (context == null) { // 可以记录日志,或者直接返回避免后续崩溃 Log.w("PlayerFragment", "Context is null, skipping addView logic"); return; } final PrefManager prefManager = new PrefManager(context); Integer id_user = 0; String key_user= ""; // 注意:这里用常量equals变量,避免getString返回null时触发NPE String loggedStatus = prefManager.getString("LOGGED"); if ("TRUE".equals(loggedStatus)) { id_user= Integer.parseInt(prefManager.getString("ID_USER")); key_user= prefManager.getString("TOKEN_USER"); } // 后续逻辑... }
2. 给PrefManager添加空值校验
在PrefManager的构造函数里提前检查传入的Context是否为空,要么抛出明确的异常方便排查,要么做降级处理:
public PrefManager(Context context) { if (context == null) { throw new IllegalArgumentException("Context cannot be null when initializing PrefManager!"); // 或者如果需要降级,使用Application Context(前提是你有自定义Application) // this._context = MyApp.getInstance().getApplicationContext(); } this._context = context; pref = _context.getSharedPreferences(PREF_NAME, PRIVATE_MODE); editor = pref.edit(); editor.apply(); }
3. 绑定ExoPlayer回调与Fragment生命周期
因为ExoPlayer的回调可能在Fragment生命周期之外执行,所以要在Fragment销毁时移除监听或释放Player资源,避免无效回调:
@Override public void onDestroy() { super.onDestroy(); if (exoPlayer != null) { // 移除你设置的Player状态监听 exoPlayer.removeListener(this); exoPlayer.release(); exoPlayer = null; } }
4. 使用Application Context代替Activity Context
如果你的SharedPreferences是全局使用的,不需要依赖Activity生命周期,可以改用Application Context,它是全局唯一且不会被回收的:
// 在PlayerFragment里获取Application Context final PrefManager prefManager = new PrefManager(requireContext().getApplicationContext());
或者在PrefManager内部自动切换到Application Context:
public PrefManager(Context context) { // 确保使用Application Context,避免Activity销毁导致的空指针 this._context = context.getApplicationContext(); pref = _context.getSharedPreferences(PREF_NAME, PRIVATE_MODE); editor = pref.edit(); editor.apply(); }
总结
这个偶发崩溃的核心是异步回调时机与Fragment生命周期不同步,导致Context失效。解决的关键是确保使用Context前做非空校验,或者使用更稳定的Application Context,同时管理好ExoPlayer的生命周期绑定,避免无效回调触发。
内容的提问来源于stack exchange,提问作者Jey

