Android API 26(Oreo)中onResume获取单例存储值空指针问题
听起来你遇到的问题大概率和Fragment生命周期的执行顺序有关,而且这个顺序在Android Oreo上的严格表现刚好踩中了你的代码逻辑漏洞。我来帮你拆解清楚:
问题根源:Fragment切换时的生命周期顺序
当你从第一个商品列表Fragment切换到第二个Fragment时,系统的生命周期调用顺序是固定的:
- 第一个Fragment执行
onPause() - 第二个Fragment执行
onResume() - 第一个Fragment执行
onStop()
你现在把选中商品的存储逻辑放在第一个Fragment的onStop()方法里,但这时候第二个Fragment的onResume()已经先执行完了——也就是说,当你在第二个Fragment里尝试从单例取值时,数据还没被存进去,自然就抛出空指针异常了。
为什么只在Oreo出现?可能是更早的Android版本中,你没遇到这个问题(比如某些场景下生命周期调度有差异,或者测试覆盖不到),但Oreo对Fragment生命周期的执行顺序管控更严格,这个固定顺序被明确执行了。
解决方案
1. 最稳妥:在用户操作完成时立即存值
不要等到Fragment生命周期回调才存数据,应该在用户选中商品的那一刻就把值存入单例。比如在列表项的点击事件里直接处理:
// 第一个Fragment中商品列表的点击事件 itemView.setOnClickListener(v -> { // 选中商品后立即存入单例 ProductSingleton.getInstance().setSelectedProduct(currentItem); // 再执行Fragment切换逻辑 switchToSecondFragment(); });
这样不管生命周期怎么调度,切换到第二个Fragment时数据已经准备好了,从根源上避免了时序问题。
2. 调整存值的生命周期时机
如果实在没办法在点击时存,也可以把存值逻辑移到第一个Fragment的onPause()方法里——因为onPause()的执行是在第二个Fragment的onResume()之前,能保证数据在第二个Fragment取值前已经被存入。
3. 验证单例实现的正确性
确保你的单例类是线程安全且不会被意外回收的,比如采用双重校验锁的标准实现:
public class ProductSingleton { private static volatile ProductSingleton instance; private Product selectedProduct; private ProductSingleton() {} public static ProductSingleton getInstance() { if (instance == null) { synchronized (ProductSingleton.class) { if (instance == null) { instance = new ProductSingleton(); } } } return instance; } public Product getSelectedProduct() { return selectedProduct; } public void setSelectedProduct(Product selectedProduct) { this.selectedProduct = selectedProduct; } }
如果单例本身实现有漏洞(比如非线程安全导致实例重复创建),也可能出现取值为空的情况,但结合你只在Oreo出现问题的描述,这个可能性相对较小。
内容的提问来源于stack exchange,提问作者user3792429

