昨天遇到一个奇葩问题, 一组不重复的数据在插入数据库的时候
数据 a 出现unique failed , 但是插入成功
数据 b 没有报错, 但是插入失败
并且发现for循环内都遍历不到数据b , 但是for循环外是可以打印到它的
就像鬼打墙一样搞了我6个小时查不出原因,
最后发现只要在插入前进行一次排序就不会出错, 虽然不明白原理, 姑且先这么把问题解决掉
<span style="font-size:14px;">public boolean insertClauseList(List<Clause> clauses,int stdNo){
// Collections.sort(clauses, new Comparator<Clause>() {
// @Override
// public int compare(Clause lhs, Clause rhs) {
// if (lhs.parentno != rhs.parentno) {
// return lhs.parentno - rhs.parentno;
// } else {
// return lhs.sortby - rhs.sortby;
// }
// }
// });
SQLiteDatabase db = helper.getWritableDatabase();
long insertResult;
ArrayList<String> errorList = new ArrayList<>();
for (Clause clause : clauses) {
ContentValues values = new ContentValues();
values.put(DBConstants.CLAUSE_NO,clause.no);
values.put(DBConstants.CLAUSE_PARENTNO,clause.parentno);
values.put(DBConstants.CLAUSE_SORTBY,clause.sortby);
values.put(DBConstants.CLAUSE_CHAPTER,clause.chapter);
values.put(DBConstants.CLAUSE_CAPTION,clause.caption);
values.put(DBConstants.CLAUSE_GENRE,clause.genre);
values.put(DBConstants.CLAUSE_ISCATALOG,clause.iscatalog);
values.put(DBConstants.CLAUSE_EXPLAIN,clause.explain);
values.put(DBConstants.CLAUSE_MANDATORY,clause.mandatory);
values.put(DBConstants.CLAUSE_FAILING,clause.failing);
values.put(DBConstants.CLAUSE_STDID,clause.stdid);
values.put(DBConstants.CLAUSE_STDNO,stdNo);
insertResult = db.insert(DBConstants.TB_CLAUSE, null, values);
if (insertResult == -1)
errorList.add(BaseApp.getContext().getString(R.string.ommitted,clause.no));
if (clause.parentno == 0){
LogUtils.w("根节点保存结果__"+insertResult);
}
}
if (errorList.size() == 0){
return true;
}else {
LogUtils.w("errorlist--"+errorList);
return false;
}
}</span>
今天换了一台新的6.0测试机, 运行到相同的位置的时候报出CurrentModifyException, 才想起这个鬼打墙是多线程访问相同资源造成的
原因是我在网络获取数据后, 把同一个list分别传递给主线程展示和子线程进行插入数据库操作
并且, 主线程的界面在展示前对list进行了一次排序, 而此时子线程正在遍历list插入数据
结果是 子线程插入数据a完成后 主线程将数据a和数据b位置对换了, 子线程访问到该位置时读取的还是数据a的内存地址, 数据b因此没有访问到
新的机器由于性能好, 多个线程操作数据的速度更快, 才暴露出问题
由此进行如下改进
1. 避免传递源数据, 使用ArrayList(List)构造复制一个list后传递
2. 对数据进行增删改的操作放到分发之前完成, 分发后只进行遍历
问题解决