Android发布版SQLite插入时报Null pointer错误该如何排查解决
问题根因
- 直接崩溃触发点:你代码中
insertMC方法的for (Menu.Prodotti prodotto : menu.prodotti)遍历逻辑,需要menu.prodotti是一个非空的List对象,当该对象为null时,系统会尝试调用null对象的iterator()方法,就抛出了你遇到的空指针异常。该报错和数据库表是否创建成功无关:如果是表不存在,报错会是table not found类的异常,和Iterator调用无关,你卸载重装后问题仍存在也验证了这一点。 - release模式独现的原因:你的ProGuard混淆规则配置不全。当前你只声明了保留
Menu类的类名,但是没有保留类的成员变量,也没有保留内部类Menu.Prodotti。如果你的Menu实例是从JSON反序列化、本地持久化读取、网络请求解析生成,混淆后成员变量prodotti的名称会被重命名,导致解析框架无法正确给该字段赋值,最终该字段为null触发崩溃。Debug模式默认关闭混淆,所以不会出现该问题。
修复方案
1. 补全ProGuard规则
修改你的proguard-rules.pro文件,增加如下规则:
# 保留Menu类所有成员变量 -keep class it.gabtamagnini.visualposmobile.models.Menu { *; } # 保留Menu所有内部类的类名和成员变量 -keep class it.gabtamagnini.visualposmobile.models.Menu$* { *; } # 如果你的实体类实现了Parcelable/Serializable,可以加通用规则避免序列化异常 -keepclassmembers class * implements android.os.Parcelable { public static final ** CREATOR; } -keepclassmembers class * implements java.io.Serializable { private static final java.io.ObjectStreamField[] serialPersistentFields; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); }
2. 代码增加空判断防护
无论什么时候,遍历集合前都建议做非空校验,避免异常:
public void insertMC(Menu menu) { database = this.getWritableDatabase(); ContentValues valuesMenu = new ContentValues(); valuesMenu.put("DESCRIPTION", menu.desc); valuesMenu.put("OPZIONALE", menu.opzionale); valuesMenu.put("LIMITE", menu.limite); valuesMenu.put("PLU", menu.plu); long id = database.insertWithOnConflict(TABLE_MC, null, valuesMenu, SQLiteDatabase.CONFLICT_REPLACE); // 增加非空判断 if (menu.prodotti != null) { for (Menu.Prodotti prodotto : menu.prodotti) { ContentValues valuesProdotto = new ContentValues(); valuesProdotto.put("DESCRIPTION", prodotto.desc); valuesProdotto.put("CODICE", prodotto.codice); valuesProdotto.put("PREZZO", prodotto.prezzo); valuesProdotto.put("QTA", prodotto.qta); valuesProdotto.put("MENU", id); valuesProdotto.put("JUMP", prodotto.jump); database.insertWithOnConflict(TABLE_MC_PRODOTTI, null, valuesProdotto, SQLiteDatabase.CONFLICT_REPLACE); } } }
Release版本调试方法
- 你已经在release配置中打开了
debuggable true,可以直接将安装了release包的设备连接到Android Studio,在Run > Attach Debugger to Android Process中选择对应应用进程,即可打断点调试。 - 混淆后的堆栈可以通过mapping文件还原:mapping文件默认保存在项目
app/build/outputs/mapping/release/mapping.txt路径,使用Android Studio内置的ProGuard retrace工具,或者SDK提供的retrace脚本,即可将混淆后的类名、行号还原为原始代码的对应信息。
内容的提问来源于stack exchange,提问作者NiceToMytyuk
相关产品推荐
相关产品推荐

