今天发现一个bug,当测试发现退出账号登录其他账号时,之前账号的数据库数据没有清除导致,数据错乱!嗯嗯,真是我的锅,我背!一般删除数据库数据有两种方案
第一种
mContext.deleteDatabase(name); name是数据库名字
第二种
SQLiteDatabase writableDatabase = helper.getWritableDatabase(); writableDatabase.delete(DbConstant.DAMAGE_INFORMATION_TABLE_NAME, null, null);//输入表名,其它参数写null writableDatabase.close();安卓的两个方法都可以删除数据库中的所有数据,第一种是直接清除指定数据库的数据,第二种是清除指定数据库的指定某个表的数据
用哪种方式,就按需选择了!
当然,你也可以直接写sql语句进行删除,不过我习惯了调用方法去执行数据库的操作!
重点并不是删除数据,而是删除数据的时机! 退出账号时,并不去删除数据,而是等到登陆时,判断退出账号时的usedid和新的账号的userid是否相同,如果不相同才去删除数据,
至于为什么大家都应该知道,我就不多费口舌了.
说到数据库,那么就肯定要说到数据库的优化!我目前在做的项目,用到了大量的数据库存储,所以,数据库的效率就非常重要!
首先要明确对后台的不合理接口进行 say no! 之前访问网络根据用户的id获取用户绑定的桥梁列表,通过列表获取各个桥梁的信息卡片以及桥梁的任务记录!
刚开始桥梁列表数量少,没有发现什么大问题,一切正常!当我进行暴力测试时,我给一个用户绑定了几百座桥梁,这个时候问题来了
这个是问题的出现以及解决思路以及遇到的问题(可略过):
首先就是主线程出现卡顿,因为获取桥梁列表后进行循环发送获取桥梁信息请求,桥梁任务记录请求!虽然都是在子线程发送请求,但是数量太大,一次循环发送两个请求,几百次循环
发送几百个*2的请求,这个时候主线程的循环就开始出现卡顿了,经过我测试,发现循环卡顿耗时的地方就在发送获取桥梁信息和发送获取任务记录,每个请求耗时大概是7-10ms,虽然
请求是在子线程操作,但是调起请求是在主线程,所以数量一旦过多,主线程依然卡顿!
为了解决这个问题,我首先想到的是将这个for循环放在子线程中,但是本来请求是使用okhttp发起,okhttp自身也维护了线程池,这么做会导致在子线程开启子线程发起请求,这样会导致
之前网络请求的框架发生问题(具体可以看我前面博文,okhttp的封装),所以只能另辟蹊径,选择okhttp使用同步请求,for循环运行在自定义的线程池中,但是问题依旧没有解决,又尝试将线程池
的最大线程数调至10,问题还是存在,甚至之后会爆出many files的异常,这种异常非常难以定位,网上查看,是说句柄超过1024,这种非常难捕获的异常!
于是,看看哪里出现了问题,发现每次获取桥梁的信息,要保存到数据库中,如果数据库存在则更新,没有则插入,数据库的操作运行在自定义的线程池中,当循环全部完成后,
部分响应刚刚回来,然后线程池将这些数据存到数据库中(又会涉及到数据库同步问题,比如重复插入数据),当达到最大线程数时,剩下的任务继续排队,于是看log会发现线程很长一段时间
(3-5s)都是在处理数据库(为了避免同步问题,我加锁了),我们知道虽然子线程操作在后台操作,但是线程数开的大,是会和主线程抢夺资源的,这就又导致了,在主线程点击按钮触发
事件,响应很慢!
看看,这些遇到的事件归结原因就是在于,网络请求数太多!为什么网络请求数过多?这是因为后台写的接口就是不合理的,没有考虑到大量桥梁的问题!后台是知道用户绑定了哪些桥梁
为何不在一次请求作为数组全部下载给用户呢!于是给后台商量了下,好嘛,获取桥梁列表后只需要发送两个请求,就搞定了!这样代码结构基本不用改,网络访问依然异步,在回调中处理,
改变的代码仅仅是返回数据的bean,并通过算法拆封!
将两个请求获取的数据,进行相应的算法进行拆分,然后保存到数据库中!
数据库的效率提高
既然是保存到数据库,首先肯定要开子线程去操作,但是当时是每次响应就要在子线程去操作一次数据库,现在是在一个响应中了,直接开启事务速度一下子就提升了,线程池也只有一个线程在工作
也不会抢夺主线程的cpu资源,点击事件也不再等待!
(sqlit每次的数据库操作,都是默认开启了事务,我们每次的操作都是会反复开启事务关闭事务,增加了io处理量,如果我们显示开启事务,大量操作后再一起提交,会非常的快捷)
BasicInfoCardDao BasicDao=new BasicInfoCardDao(mContext, DbConstant.BRIDGE_BASIC_INFOCARD_TABLE_NAME); SQLiteDatabase writableDatabase = BasicDao.db; writableDatabase.beginTransaction();//开启事务 Log.e("开始连接网络",System.currentTimeMillis()-s+""+"准备插入数据"); try { for (EngineeringInfoSimple engineeringInfoSimple : list) { String engin = engineeringInfoSimple.getId().toString(); BridgeInfo bridgeInfo = engineeringInfoSimple.getBridgeInfo(); //等于null说明之前没有该基本卡的数据 // ,那就进行插入 if(BasicDao.query(engin)==null){ BasicDao.insert(engin,bridgeInfo); // Log.e("BasicInfoCardDao","插入"); }else{ BasicDao.update(bridgeInfo,engin); // Log.e("BasicInfoCardDao","更新"); } } writableDatabase.setTransactionSuccessful();//打个标记证明成功 }catch (Exception e) { throw e; } finally { writableDatabase.endTransaction();//结束事务 writableDatabase.close(); Log.e("开始连接网络",System.currentTimeMillis()-s+""+"数据全部查如完成耗时"); } } });如上代码,当时测试,不开启事务和开启事务后的速度大概相差5倍,当操作数据库的次数越多,开启事务和不开启事务的速度相差的也就越大!
但是还没完,可以看到我的代码是每次查询一次再进行插入和更新,是不是觉得很繁琐,为什么每次都要查询在判断是需要插入还是更新呢?难道就没有其它方式么?
有的,这又是数据库的优化了!(我的需求是,数据库存在的数据则进行更新,不存在的则进行插入)
首先 在创建数据库时需要建立唯一索引,比如我是根据桥梁的id(每个桥梁在后台都有唯一的id)建立唯一索引 所以要修改sql语句添加NOT NULL UNIQUE,如下
String sql1="create table "+ DbConstant.BRIDGE_BASIC_INFOCARD_TABLE_NAME +"(_id integer primary key autoincrement ,"+ DbConstant.CODE+" nvarchar(50) NOT NULL UNIQUE,"+DbConstant.BASIC_INFOCARD_OBJ+" text)";
如果你的app已经发布了,没关系在
onUpgrade方法中对数据进行升级,注意将数据库版本参数+1
public BasicInfoCardDb(Context context,String tableName) { super(context, "basicinfocard.db", null, 1);//1要改变,比1大就行,一般在原来的基础上+1 }
然后再插入数据库时,调用这个方法,就不用插入前(更新前)先查询一遍消耗性能!@Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { String sqlAlter ="ALTER TABLE "+ DbConstant.BRIDGE_BASIC_INFOCARD_TABLE_NAME +" ADD UNIQUE "+DbConstant.CODE; sqLiteDatabase.execSQL(sqlAlter); Log.e("数据库升级","修改数据库生效"); }
现在来看看四种情况下的性能对比(其实就是耗时对比) 四种情况 :long result = db.replace(mName,null,values);//该方法就替代了先查询后插入(更新),提高了性能
1 未开启事务 ,未开启唯一约束 耗时3秒
2 未开启事务,但开启了唯一约束 耗时1.8秒
3 开启事务,未开启唯一约束 耗时0.324秒
4 开启事务,开启唯一约束 耗时0.2秒
上述四种情况,可以显著看见开启事务效率非常明显,开启了唯一约束效率也有提升,但是数据只有几百条,,如果数据条上万条,二者提升的效率都是非常可观的
当然除了开启事务,以及唯一约束提高效率外.在sqlite3关闭写同步,进行执行准备,效率更加更加高效!不过这两个跟我们无关,Android开发掌握事务和唯一约束就可以了