Android Retrofit2+RxJava2存SQLite时出现空指针异常求助
排查Retrofit+RxJava2项目中
savetodb方法的空指针异常 问题背景
我维护的一个Android项目采用Retrofit2+RxJava2实现接口调用,流程是:登录接口调用成功后触发datadump(),进而调用gedistrict()、gettaluka()、getvillage()、getofficetype()四个接口获取地区类数据,最后通过savetodb()将数据存入SQLite数据库。但运行时触发了空指针异常,错误栈指向Dept_login.savetodb(Dept_login.java:144)。
核心代码片段
onCreate初始化
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_dept); mAPIService = ApiUtils.getAPIService(); }
登录接口调用
public void sendPost() { mAPIService.savePost(params).enqueue(new Callback<Example>() { @Override public void onResponse(Call<Example> call, Response<Example> response) { if(response.isSuccessful()) { Example ex =response.body(); // 日志打印与数据解析逻辑 if(ex.getMessage().contains("Successful")){ datadump(); } else{ Toast.makeText(getApplicationContext(),"Something went wrong! Try again later!",Toast.LENGTH_SHORT).show(); } } } @Override public void onFailure(Call<Example> call, Throwable t) { t.printStackTrace(); Log.e(TAG, "Unable to submit post to API."); } }); }
数据入库方法
private void savetodb(){ //District Table for(int i=0;i<dist.size();i++) mydb.insertdataindistrictdb(dist.get(i).getStateId(),dist.get(i).getDistrictId(),dist.get(i).getEngName(),dist.get(i).getMarName()); //Taluka Table for(int i=0;i<taluk.size();i++) mydb.insertdataintalukadb(taluk.get(i).getStateId(),taluk.get(i).getDistrictId(),taluk.get(i).getTalukaId(),taluk.get(i).getEngName(),taluk.get(i).getMarName()); //Village Table for(int i=0;i<villa.size();i++) mydb.insertdatainvillagedb(villa.get(i).getStateId(),villa.get(i).getDistrictId(),villa.get(i).getTalukaId(),villa.get(i).getVillageId(),villa.get(i).getEngName(),villa.get(i).getMarName()); //Office Type Table for(int i=0;i<oft.size();i++) mydb.insertdatainofficetypedb(oft.get(i).getOfficeType(),oft.get(i).getEngOfficeTypeName(),oft.get(i).getMarOfficeTypeName()); Log.e(TAG,"Database Create"); }
错误栈信息
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.trng18.myapplication, PID: 2424 java.lang.NullPointerException at com.example.trng18.myapplication.Dept_login.savetodb(Dept_login.java:144) at com.example.trng18.myapplication.Dept_login.datadump(Dept_login.java:138) at com.example.trng18.myapplication.Dept_login.access$100(Dept_login.java:42) at com.example.trng18.myapplication.Dept_login$1.onResponse(Dept_login.java:93)
空指针原因定位
结合错误栈和代码逻辑,空指针大概率源于以下三种场景:
- 数据库实例
mydb未初始化:调用mydb.insertdataindistrictdb()等方法前,没有给mydb赋值(比如忘记在onCreate中初始化数据库Helper)。 - 数据集合为null:
dist、taluk、villa、oft中的某一个未提前初始化(比如没有用new ArrayList<>()创建实例),或者RxJava接口调用失败导致集合始终为null,调用size()时触发异常。 - 集合元素为null:比如
dist.get(i)返回null,后续调用getStateId()等方法时触发空指针。
解决方案与优化建议
1. 确保数据库实例初始化
在onCreate中提前初始化数据库实例,避免mydb为null:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_dept); mAPIService = ApiUtils.getAPIService(); // 替换成你的数据库Helper类名 mydb = new YourDbHelperImplementation(this); }
2. 提前初始化数据集合
在类的成员变量定义处,为所有集合创建空实例,避免后续调用size()时触发空指针:
private List<District> dist = new ArrayList<>(); private List<Taluka> taluk = new ArrayList<>(); private List<Village> villa = new ArrayList<>(); private List<OfficeType> oft = new ArrayList<>();
3. 增加空值判断逻辑
在savetodb中添加多层空值检查,覆盖数据库实例、集合、集合元素的空值场景:
private void savetodb(){ // 先检查数据库实例是否为空 if(mydb == null) { Log.e(TAG, "Database instance is null!"); return; } // District表入库:检查集合和元素非空 if(dist != null && !dist.isEmpty()){ for(District district : dist) { if(district != null){ mydb.insertdataindistrictdb( district.getStateId(),district.getDistrictId(), district.getEngName(),district.getMarName() ); } } } // Taluka表入库:同理添加空值检查 if(taluk != null && !taluk.isEmpty()){ for(Taluka taluka : taluk) { if(taluka != null){ mydb.insertdataintalukadb( taluka.getStateId(),taluka.getDistrictId(),taluka.getTalukaId(), taluka.getEngName(),taluka.getMarName() ); } } } // Village表入库:同理添加空值检查 if(villa != null && !villa.isEmpty()){ for(Village village : villa) { if(village != null){ mydb.insertdatainvillagedb( village.getStateId(),village.getDistrictId(),village.getTalukaId(), village.getVillageId(),village.getEngName(),village.getMarName() ); } } } // Office Type表入库:同理添加空值检查 if(oft != null && !oft.isEmpty()){ for(OfficeType officeType : oft) { if(officeType != null){ mydb.insertdatainofficetypedb( officeType.getOfficeType(),officeType.getEngOfficeTypeName(), officeType.getMarOfficeTypeName() ); } } } Log.e(TAG,"Database Create"); }
4. 规范RxJava接口调用的错误处理
在gedistrict()等RxJava接口调用中,添加空值检查和错误处理,确保集合始终处于可用状态:
private void getdistrict() { mAPIService.getDistrict() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<List<District>>() { @Override public void onSubscribe(Disposable d) {} @Override public void onNext(List<District> districts) { // 数据非空才更新集合 if(districts != null){ dist.clear(); dist.addAll(districts); } } @Override public void onError(Throwable e) { Log.e(TAG, "Failed to get district data: " + e.getMessage()); // 错误时初始化空集合,避免后续调用size()报错 dist = new ArrayList<>(); } @Override public void onComplete() {} }); }
5. 优化数据加载时机(可选)
使用RxJava的zip操作符,等待四个接口全部返回数据后再执行入库,避免因部分接口未返回导致的空数据问题:
private void datadump() { Observable.zip( mAPIService.getDistrict().subscribeOn(Schedulers.io()), mAPIService.getTaluka().subscribeOn(Schedulers.io()), mAPIService.getVillage().subscribeOn(Schedulers.io()), mAPIService.getOfficeType().subscribeOn(Schedulers.io()), (districts, talukas, villages, officeTypes) -> { // 批量更新集合 if(districts != null) dist.addAll(districts); if(talukas != null) taluk.addAll(talukas); if(villages != null) villa.addAll(villages); if(officeTypes != null) oft.addAll(officeTypes); return null; } ) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Object>() { @Override public void onSubscribe(Disposable d) {} @Override public void onNext(Object o) { savetodb(); // 所有数据加载完成后再入库 } @Override public void onError(Throwable e) { Log.e(TAG, "Data dump failed: " + e.getMessage()); Toast.makeText(getApplicationContext(), "Failed to load data", Toast.LENGTH_SHORT).show(); } @Override public void onComplete() {} }); }
内容的提问来源于stack exchange,提问作者Saman
相关产品推荐
相关产品推荐

