精华帖 (0) :: 良好帖 (0) :: 新手帖 (2) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-11-19
一,前言 没有采用Android自身提供的那一套数据库操作方式。而是想对SQLite数据库文件有更全面的控制,包括随时导出数据库文件修改表结构,增删数据等等。这样一来虽然在开放中得到不少便利,但是也带来了数据库升级的一些问题。 后来不得已采用了一种方案,可以解决问题,现将方案的全部实现细节记录下来。最后也会提出一些我认为有问题的地方。
二,数据库文件拷贝 程序不负责数据库的创建,SQLite数据库文件是在外部创建好的。程序启动阶段拷贝进SD卡。以达到对数据库结构的全面控制。 数据库文件存放位置见附件图片。 public void copyDBFile() { // 数据库路径 if (!FileOperator.checkFile(SysConst.DB_PATH)) { boolean result = FileOperator.write2Sdcard(activity, R.raw.scpip_collection, SysConst.DB_PATH); Debug.log("无数据库文件,首次拷贝" + result); if (!result) { throw new IllegalAccessError(activity .getString(R.string.copy_db_exception)); } else { // 拷贝成功,更新数据库版本 try { PackageInfo info = activity.getPackageManager() .getPackageInfo(activity.getPackageName(), 0); // 当前程序版本号,在AndroidManifest.xml中定义 int versionCode = info.versionCode; Config.saveDbVer(versionCode); Debug.log("拷贝成功" + result); } catch (NameNotFoundException e) { Debug.e(e); } } } else { // 数据库已存在的情况 if (dbUpdate.needUpdate() ) { activity.showProgress("数据库升级中,请稍后", false); new Thread() { @Override public void run() { try { Debug.log("update db"); dbUpdate.updateDb(); handler.sendEmptyMessage(0); } catch (Exception e) { Debug.e(e); handler.sendEmptyMessage(-1); } }; }.start(); } } } 其中有几个要点: 1、检测数据库文件是否已经存在。不存在则从raw文件夹复制数据库文件拷贝至SD卡中指定目录。 2、数据库版本是根据应用的versionCode相同。拷贝数据库后,会把当前versionCode写入数据库的表中。 <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.xxx" android:versionCode="2" android:versionName="1.01.001"> <uses-sdk android:minSdkVersion="8" /> versionCode在AndroidManifest.xml文件中。 在这种方案下,实际上是由versionCode控制数据库版本,versionName控制程序版本。 3、SD卡指定目录已经存在数据库文件的情况,则读取其中保存的数据库版本号,与versionCode对比,从而确定是否需要升级数据库。代码如下: public boolean needUpdate() { int currVer = Config.getDbVer(); return currVer < getAppVersion(); } public int getAppVersion(){ try { PackageInfo info = context.getPackageManager().getPackageInfo( context.getPackageName(), 0); // 当前程序版本号,在AndroidManifest.xml中定义 return info.versionCode; } catch (NameNotFoundException e) { Debug.e(e); return 1; } }
三,升级数据库 包括三个步骤: 1、从程序中拷贝新数据库文件至SD卡指定目录,命名为temp.db。 String temp = SysConst.DB_FOLDER + "temp.db"; boolean s1 = FileOperator.write2Sdcard(context, R.raw.scpip_collection,temp);
2、分别获取两个数据源。 //原数据库文件 BaseDao sd = new BaseDao(); //新数据库文件 BaseDao nd = new BaseDao(temp); 对于SQLite数据库来讲,数据源就是数据库文件。BaseDao是自己封装的,关键在于要可以配置不同的数据源 具体实现的相关代码如下: private String dbPath; public BaseDao() {} public BaseDao(String dbPath) { this.dbPath = dbPath; } public SQLiteDatabase getDb() { return SQLiteDatabase.openDatabase(SysConst.DB_PATH, null, SQLiteDatabase.OPEN_READWRITE); } public SQLiteDatabase getDb(String dbPath) { return SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READWRITE); }
这样就可以根据文件,获取不同的SQLiteDatabase 对象。
3、传输数据 把原数据库中的数据查询出来,插入到新数据库中。 public <E>void transfer(BaseDao sd,BaseDao nd,Class<E> cls) throws Exception{ List<E> list = sd.find(cls, null); nd.batchInsert(list); }
这里有两个要点, 第一,使用了自行封装的ORM。 第二,使用了SQLite批量插入,增加写入效率。代码如下: @Override public <T> void batchInsert(List<T> datas) throws Exception { SQLiteDatabase dba = null; if(dbPath == null){ dba = getDb(); } else { dba = getDb(dbPath); } int size = datas.size(); try { dba.beginTransaction(); for (int i = 0; i < size; i++) { DaoHelper helper = new DaoHelper(datas.get(i)); String tableName = helper.getTableName(); String sql = "select 1 from " + tableName + " where " + helper.getPkCol() + "="; Object id = helper.getPkValue(); if (id instanceof String) { sql = sql + "'" + id + "'"; } else { sql = sql + id; } c = dba.rawQuery(sql, null); if (c != null ? (c.getCount() == 1) : false) { c.close(); continue; } if(c != null){ c.close(); } // SqlArgs sa = helper.prepareInsert(); // dba.execSQL(sa.getSql(), sa.getArgs()); dba.insert(helper.getTableName(), "", helper.getInsertContent()); } dba.setTransactionSuccessful(); dba.endTransaction(); } finally { dba.close(); } }
4、写入当前数据库版本,即从程序获得的versionCode。 5、删除原数据库文件,重命名temp.db。 完整过程如下: public void updateDb() throws Exception { //1,写入新的数据库文件,命名为temp String temp = SysConst.DB_FOLDER + "temp.db"; boolean s1 = FileOperator.write2Sdcard(context, R.raw.scpip_collection,temp); Debug.log("s1:" + s1); if(s1) { //原数据库文件 BaseDao sd = new BaseDao(); //新数据库文件 BaseDao nd = new BaseDao(temp); //转移数据 //此处代码略 //删除原数据库文件,重命名临时数据库文件 if(FileOperator.delSdcardFile(SysConst.DB_PATH)){ File file = new File(temp); file.renameTo(new File(SysConst.DB_PATH)); } //此时更新数据库版本 Config.saveDbVer(getAppVersion()); } } 至此,整个数据库升级完成。在保留原数据的基础上,获取了新的数据库结构。
四,问题 1,需要保留的原数据与新表结构不符。这个可以在程序中控制,创建新的OR映射。 2,效率问题,如果需要保留的数据量非常大的情况下,是否会出现问题。这个是亟待解决的,目前我还没有想到解决办法。
五,总结 这个方案针对的情况是外部创建数据库文件,程序启动时从apk包将数据库文件拷贝进SD卡,从而达到对数据库文件的完全控制。 方案步骤: 1,检测是否需要升级数据库文件。数据库文件版本是由versionCode控制。程序升级时,如果需要升级数据库,则要将versionCode+1。 2,将新数据库文件(存在于apk中),写入SD卡。 3,转移数据。 4,删除原数据库文件,重命名新数据库文件。 5,向新数据库写入当前数据库版本。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2012-11-20
求具体 实现代码 yzsunlight@qq.com
|
|
返回顶楼 | |
浏览 6187 次